Kgshop commited on
Commit
f666613
·
verified ·
1 Parent(s): 3e23eaa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -88
app.py CHANGED
@@ -180,9 +180,9 @@ def load_data():
180
 
181
  for emp in data['employees']:
182
  if 'pin' not in emp: emp['pin'] = '0000'
183
- if 'daily_rate' not in emp: emp['daily_rate'] = 230000
184
- if 'target_amount' not in emp: emp['target_amount'] = 1500000
185
- if 'bonus_percentage' not in emp: emp['bonus_percentage'] = 10
186
 
187
  if not os.path.exists(DATA_FILE):
188
  try:
@@ -1196,7 +1196,7 @@ SALARY_TEMPLATE = '''
1196
  label { display: block; margin-bottom: 5px; color: var(--text-muted); font-size: 0.85rem; font-weight: 600; }
1197
 
1198
  .rep-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
1199
- .rep-table th, .rep-table td { padding: 8px 5px; border-bottom: 1px solid var(--border); text-align: left; }
1200
  .rep-table th { color: var(--text-muted); font-weight: normal; }
1201
 
1202
  @media (max-width: 600px) {
@@ -1211,50 +1211,36 @@ SALARY_TEMPLATE = '''
1211
  <h1><i class="fas fa-money-bill-wave"></i> Отчет по ЗП</h1>
1212
  <a href="/admin" class="btn btn-dark"><i class="fas fa-arrow-left"></i> Назад в админку</a>
1213
  </div>
1214
-
1215
  <div class="report-section">
1216
- <h3 style="color:var(--primary); margin-top:0;">Управление штрафами</h3>
1217
- <form method="POST" style="display:flex; gap:10px; flex-wrap:wrap; margin-bottom: 20px;">
1218
  <input type="hidden" name="action" value="add_fine">
1219
- <select name="employee_id" required style="flex:1; min-width:150px;">
1220
- <option value="">Выберите сотрудника</option>
1221
- {% for emp in employees %}
1222
- <option value="{{ emp.id }}">{{ emp.name }}</option>
1223
- {% endfor %}
1224
- </select>
1225
- <input type="date" name="date" id="fineDate" required style="flex:1; min-width:150px;">
1226
- <input type="number" name="amount" placeholder="Сумма штрафа" required style="flex:1; min-width:150px;">
1227
- <input type="text" name="reason" placeholder="Причина" required style="flex:2; min-width:200px;">
1228
- <button type="submit" class="btn btn-danger"><i class="fas fa-plus"></i> Добавить штраф</button>
 
 
 
 
 
 
 
 
 
 
 
1229
  </form>
1230
- <div style="overflow-x:auto;">
1231
- <table class="rep-table">
1232
- <tr><th>Дата</th><th>Сотрудник</th><th>Причина</th><th>Сумма</th><th>Действие</th></tr>
1233
- {% for fine in fines|sort(attribute='date', reverse=True) %}
1234
- <tr>
1235
- <td>{{ fine.date }}</td>
1236
- <td>
1237
- {% for emp in employees %}
1238
- {% if emp.id == fine.employee_id %}{{ emp.name }}{% endif %}
1239
- {% endfor %}
1240
- </td>
1241
- <td>{{ fine.reason }}</td>
1242
- <td>{{ fine.amount }} {{ currency_code }}</td>
1243
- <td>
1244
- <form method="POST" onsubmit="return confirm('Удалить штраф?');" style="margin:0;">
1245
- <input type="hidden" name="action" value="delete_fine">
1246
- <input type="hidden" name="fine_id" value="{{ fine.id }}">
1247
- <button type="submit" class="btn btn-dark" style="padding:5px 10px; font-size:0.8rem;"><i class="fas fa-trash" style="color:var(--danger);"></i></button>
1248
- </form>
1249
- </td>
1250
- </tr>
1251
- {% endfor %}
1252
- </table>
1253
- </div>
1254
  </div>
1255
-
1256
  <div class="report-section">
1257
- <h3 style="color:var(--primary); margin-top:0;">Расчет зарплаты</h3>
1258
  <div class="form-row" style="margin-bottom: 20px; align-items: flex-end;">
1259
  <div>
1260
  <label>Дата начала</label>
@@ -1270,13 +1256,19 @@ SALARY_TEMPLATE = '''
1270
  <table class="rep-table" id="tableSalary"></table>
1271
  </div>
1272
  </div>
 
 
 
 
 
 
 
1273
  </div>
1274
 
1275
  <script>
1276
  const allOrders = {{ orders_json|safe }};
1277
- const workdays = {{ workdays_json|safe }};
1278
  const employees = {{ employees_json|safe }};
1279
- const fines = {{ fines_json|safe }};
1280
  const currencyCode = '{{ currency_code }}';
1281
 
1282
  function getLocalDateStr(d) {
@@ -1289,7 +1281,6 @@ SALARY_TEMPLATE = '''
1289
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
1290
  document.getElementById('salStart').value = getLocalDateStr(firstDay);
1291
  document.getElementById('salEnd').value = todayStr;
1292
- document.getElementById('fineDate').value = todayStr;
1293
  generateSalaryReport();
1294
  }
1295
 
@@ -1309,12 +1300,45 @@ SALARY_TEMPLATE = '''
1309
  empSales[o.employee_id][oDate] += o.total_price;
1310
  }
1311
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1312
 
1313
  employees.forEach(emp => {
1314
  let daysWorked = 0;
1315
  let fixedSal = 0;
1316
  let bonusSal = 0;
1317
- let employeeFines = 0;
1318
 
1319
  let currDate = new Date(start);
1320
  const endDate = new Date(end);
@@ -1325,16 +1349,10 @@ SALARY_TEMPLATE = '''
1325
 
1326
  if (dailySales > 0) {
1327
  daysWorked++;
 
1328
 
1329
- let rate = parseFloat(emp.daily_rate);
1330
- if (isNaN(rate) || rate === 0) rate = 230000;
1331
- fixedSal += rate;
1332
-
1333
- let target = parseFloat(emp.target_amount);
1334
- if (isNaN(target) || target === 0) target = 1500000;
1335
-
1336
- let pct = parseFloat(emp.bonus_percentage);
1337
- if (isNaN(pct) || pct === 0) pct = 10;
1338
 
1339
  if (pct > 0 && dailySales > target) {
1340
  bonusSal += (dailySales - target) * (pct / 100);
@@ -1342,22 +1360,18 @@ SALARY_TEMPLATE = '''
1342
  }
1343
  currDate.setDate(currDate.getDate() + 1);
1344
  }
1345
-
1346
- fines.forEach(f => {
1347
- if (f.employee_id === emp.id && f.date >= start && f.date <= end) {
1348
- employeeFines += parseFloat(f.amount);
1349
- }
1350
- });
1351
 
1352
- const total = fixedSal + bonusSal - employeeFines;
1353
- if (daysWorked > 0 || total !== 0 || employeeFines > 0) {
 
 
1354
  html += `<tr>
1355
  <td>${emp.name}</td>
1356
  <td>${daysWorked}</td>
1357
  <td>${fixedSal.toFixed(2)} ${currencyCode}</td>
1358
  <td>${bonusSal.toFixed(2)} ${currencyCode}</td>
1359
- <td style="color:var(--danger);">${employeeFines.toFixed(2)} ${currencyCode}</td>
1360
- <td style="font-weight:bold; color:var(--success);">${total.toFixed(2)} ${currencyCode}</td>
1361
  </tr>`;
1362
  }
1363
  });
@@ -1516,9 +1530,9 @@ ADMIN_TEMPLATE = '''
1516
  <input type="hidden" name="action" value="add_employee">
1517
  <div style="flex:1; min-width:150px;"><label>Имя</label><input type="text" name="employee_name" required autocomplete="off"></div>
1518
  <div style="flex:1; min-width:100px;"><label>PIN (4 цифры)</label><input type="text" name="pin" pattern="\\d{4}" required autocomplete="off"></div>
1519
- <div style="flex:1; min-width:100px;"><label>Ставка за день</label><input type="number" name="daily_rate" value="230000" required></div>
1520
- <div style="flex:1; min-width:100px;"><label>Цель (n сумма)</label><input type="number" name="target_amount" value="1500000" required></div>
1521
- <div style="flex:1; min-width:100px;"><label>Бонус (%)</label><input type="number" name="bonus_percentage" value="10" step="0.1" required></div>
1522
  <button type="submit" class="btn btn-dark" style="width:100%; margin-top:10px;"><i class="fas fa-plus"></i> Добавить</button>
1523
  </form>
1524
  </div>
@@ -1958,14 +1972,18 @@ def admin_salary():
1958
  if request.method == 'POST':
1959
  action = request.form.get('action')
1960
  if action == 'add_fine':
 
 
 
 
1961
  if 'fines' not in data:
1962
  data['fines'] = []
1963
  data['fines'].append({
1964
  'id': uuid4().hex,
1965
- 'employee_id': request.form.get('employee_id'),
1966
- 'date': request.form.get('date'),
1967
- 'amount': float(request.form.get('amount', 0)),
1968
- 'reason': request.form.get('reason', '')
1969
  })
1970
  save_data(data)
1971
  elif action == 'delete_fine':
@@ -1977,17 +1995,15 @@ def admin_salary():
1977
 
1978
  orders = data.get('orders', {})
1979
  employees = data.get('employees', [])
1980
- workdays = data.get('workdays', {})
1981
  fines = data.get('fines', [])
1982
 
1983
  return render_template_string(
1984
  SALARY_TEMPLATE,
1985
  orders_json=json.dumps(list(orders.values())),
1986
  employees_json=json.dumps(employees),
1987
- workdays_json=json.dumps(workdays),
1988
  fines_json=json.dumps(fines),
1989
  employees=employees,
1990
- fines=fines,
1991
  currency_code=CURRENCY_CODE
1992
  )
1993
 
@@ -2016,12 +2032,12 @@ def admin():
2016
  elif action == 'add_employee':
2017
  emp_name = request.form.get('employee_name', '').strip()
2018
  pin = request.form.get('pin', '0000').strip()
2019
- try: daily_rate = float(request.form.get('daily_rate', 230000))
2020
- except: daily_rate = 230000
2021
- try: target_amount = float(request.form.get('target_amount', 1500000))
2022
- except: target_amount = 1500000
2023
- try: bonus_percentage = float(request.form.get('bonus_percentage', 10))
2024
- except: bonus_percentage = 10
2025
 
2026
  if emp_name:
2027
  employees.append({
@@ -2041,12 +2057,12 @@ def admin():
2041
  if e.get('id') == emp_id:
2042
  e['name'] = request.form.get('name', '').strip()
2043
  e['pin'] = request.form.get('pin', '0000').strip()
2044
- try: e['daily_rate'] = float(request.form.get('daily_rate', 230000))
2045
- except: e['daily_rate'] = 230000
2046
- try: e['target_amount'] = float(request.form.get('target_amount', 1500000))
2047
- except: e['target_amount'] = 1500000
2048
- try: e['bonus_percentage'] = float(request.form.get('bonus_percentage', 10))
2049
- except: e['bonus_percentage'] = 10
2050
  break
2051
  data['employees'] = employees
2052
  save_data(data)
 
180
 
181
  for emp in data['employees']:
182
  if 'pin' not in emp: emp['pin'] = '0000'
183
+ if 'daily_rate' not in emp: emp['daily_rate'] = 0
184
+ if 'target_amount' not in emp: emp['target_amount'] = 0
185
+ if 'bonus_percentage' not in emp: emp['bonus_percentage'] = 0
186
 
187
  if not os.path.exists(DATA_FILE):
188
  try:
 
1196
  label { display: block; margin-bottom: 5px; color: var(--text-muted); font-size: 0.85rem; font-weight: 600; }
1197
 
1198
  .rep-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
1199
+ .rep-table th, .rep-table td { padding: 8px 5px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; }
1200
  .rep-table th { color: var(--text-muted); font-weight: normal; }
1201
 
1202
  @media (max-width: 600px) {
 
1211
  <h1><i class="fas fa-money-bill-wave"></i> Отчет по ЗП</h1>
1212
  <a href="/admin" class="btn btn-dark"><i class="fas fa-arrow-left"></i> Назад в админку</a>
1213
  </div>
1214
+
1215
  <div class="report-section">
1216
+ <h3 style="color:var(--primary); margin-top:0;">Добавить штраф</h3>
1217
+ <form method="POST" style="display:flex; gap:10px; flex-wrap:wrap; align-items:flex-end;">
1218
  <input type="hidden" name="action" value="add_fine">
1219
+ <div style="flex:1; min-width:150px;">
1220
+ <label>Сотрудник</label>
1221
+ <select name="employee_id" required>
1222
+ {% for emp in employees %}
1223
+ <option value="{{ emp.id }}">{{ emp.name }}</option>
1224
+ {% endfor %}
1225
+ </select>
1226
+ </div>
1227
+ <div style="flex:1; min-width:100px;">
1228
+ <label>Сумма</label>
1229
+ <input type="number" name="amount" required min="0" step="0.01">
1230
+ </div>
1231
+ <div style="flex:1; min-width:150px;">
1232
+ <label>Дата</label>
1233
+ <input type="date" name="date" required value="{{ current_date }}">
1234
+ </div>
1235
+ <div style="flex:2; min-width:200px;">
1236
+ <label>Причина</label>
1237
+ <input type="text" name="reason">
1238
+ </div>
1239
+ <button type="submit" class="btn btn-danger" style="height:44px;"><i class="fas fa-minus-circle"></i> Штраф</button>
1240
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1241
  </div>
1242
+
1243
  <div class="report-section">
 
1244
  <div class="form-row" style="margin-bottom: 20px; align-items: flex-end;">
1245
  <div>
1246
  <label>Дата начала</label>
 
1256
  <table class="rep-table" id="tableSalary"></table>
1257
  </div>
1258
  </div>
1259
+
1260
+ <div class="report-section" style="margin-top: 20px;">
1261
+ <h3 style="color:var(--danger); margin-top:0;">История штрафов за период</h3>
1262
+ <div style="overflow-x:auto;">
1263
+ <table class="rep-table" id="tableFines"></table>
1264
+ </div>
1265
+ </div>
1266
  </div>
1267
 
1268
  <script>
1269
  const allOrders = {{ orders_json|safe }};
 
1270
  const employees = {{ employees_json|safe }};
1271
+ const allFines = {{ fines_json|safe }};
1272
  const currencyCode = '{{ currency_code }}';
1273
 
1274
  function getLocalDateStr(d) {
 
1281
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
1282
  document.getElementById('salStart').value = getLocalDateStr(firstDay);
1283
  document.getElementById('salEnd').value = todayStr;
 
1284
  generateSalaryReport();
1285
  }
1286
 
 
1300
  empSales[o.employee_id][oDate] += o.total_price;
1301
  }
1302
  });
1303
+
1304
+ let empFines = {};
1305
+ let finesHtml = '<tr><th>Дата</th><th>Сотрудник</th><th>Сумма</th><th>Причина</th><th>Действие</th></tr>';
1306
+ let hasFines = false;
1307
+
1308
+ allFines.forEach(f => {
1309
+ if (f.date >= start && f.date <= end) {
1310
+ hasFines = true;
1311
+ if (!empFines[f.employee_id]) empFines[f.employee_id] = 0;
1312
+ empFines[f.employee_id] += parseFloat(f.amount);
1313
+
1314
+ const emp = employees.find(e => e.id === f.employee_id);
1315
+ const empName = emp ? emp.name : 'Удален';
1316
+
1317
+ finesHtml += `<tr>
1318
+ <td>${f.date}</td>
1319
+ <td>${empName}</td>
1320
+ <td style="color:var(--danger);">${parseFloat(f.amount).toFixed(2)} ${currencyCode}</td>
1321
+ <td>${f.reason}</td>
1322
+ <td>
1323
+ <form method="POST" style="margin:0;" onsubmit="return confirm('Удалить штраф?');">
1324
+ <input type="hidden" name="action" value="delete_fine">
1325
+ <input type="hidden" name="fine_id" value="${f.id}">
1326
+ <button type="submit" class="btn btn-danger" style="padding:4px 8px; font-size:0.8rem; border-radius:6px;"><i class="fas fa-trash"></i></button>
1327
+ </form>
1328
+ </td>
1329
+ </tr>`;
1330
+ }
1331
+ });
1332
+
1333
+ if (!hasFines) {
1334
+ finesHtml += '<tr><td colspan="5">Нет штрафов за выбранный период</td></tr>';
1335
+ }
1336
+ document.getElementById('tableFines').innerHTML = finesHtml;
1337
 
1338
  employees.forEach(emp => {
1339
  let daysWorked = 0;
1340
  let fixedSal = 0;
1341
  let bonusSal = 0;
 
1342
 
1343
  let currDate = new Date(start);
1344
  const endDate = new Date(end);
 
1349
 
1350
  if (dailySales > 0) {
1351
  daysWorked++;
1352
+ fixedSal += parseFloat(emp.daily_rate || 0);
1353
 
1354
+ const target = parseFloat(emp.target_amount || 0);
1355
+ const pct = parseFloat(emp.bonus_percentage || 0);
 
 
 
 
 
 
 
1356
 
1357
  if (pct > 0 && dailySales > target) {
1358
  bonusSal += (dailySales - target) * (pct / 100);
 
1360
  }
1361
  currDate.setDate(currDate.getDate() + 1);
1362
  }
 
 
 
 
 
 
1363
 
1364
+ const totalFines = empFines[emp.id] || 0;
1365
+ const total = fixedSal + bonusSal - totalFines;
1366
+
1367
+ if (daysWorked > 0 || total !== 0 || totalFines > 0) {
1368
  html += `<tr>
1369
  <td>${emp.name}</td>
1370
  <td>${daysWorked}</td>
1371
  <td>${fixedSal.toFixed(2)} ${currencyCode}</td>
1372
  <td>${bonusSal.toFixed(2)} ${currencyCode}</td>
1373
+ <td style="color:var(--danger);">${totalFines > 0 ? '-' + totalFines.toFixed(2) : '0.00'} ${currencyCode}</td>
1374
+ <td style="font-weight:bold; color:${total >= 0 ? 'var(--success)' : 'var(--danger)'};">${total.toFixed(2)} ${currencyCode}</td>
1375
  </tr>`;
1376
  }
1377
  });
 
1530
  <input type="hidden" name="action" value="add_employee">
1531
  <div style="flex:1; min-width:150px;"><label>Имя</label><input type="text" name="employee_name" required autocomplete="off"></div>
1532
  <div style="flex:1; min-width:100px;"><label>PIN (4 цифры)</label><input type="text" name="pin" pattern="\\d{4}" required autocomplete="off"></div>
1533
+ <div style="flex:1; min-width:100px;"><label>Ставка за день</label><input type="number" name="daily_rate" value="0" required></div>
1534
+ <div style="flex:1; min-width:100px;"><label>Цель (n сумма)</label><input type="number" name="target_amount" value="0" required></div>
1535
+ <div style="flex:1; min-width:100px;"><label>Бонус (%)</label><input type="number" name="bonus_percentage" value="0" step="0.1" required></div>
1536
  <button type="submit" class="btn btn-dark" style="width:100%; margin-top:10px;"><i class="fas fa-plus"></i> Добавить</button>
1537
  </form>
1538
  </div>
 
1972
  if request.method == 'POST':
1973
  action = request.form.get('action')
1974
  if action == 'add_fine':
1975
+ emp_id = request.form.get('employee_id')
1976
+ amount = float(request.form.get('amount', 0))
1977
+ date = request.form.get('date', get_current_date())
1978
+ reason = request.form.get('reason', '')
1979
  if 'fines' not in data:
1980
  data['fines'] = []
1981
  data['fines'].append({
1982
  'id': uuid4().hex,
1983
+ 'employee_id': emp_id,
1984
+ 'amount': amount,
1985
+ 'date': date,
1986
+ 'reason': reason
1987
  })
1988
  save_data(data)
1989
  elif action == 'delete_fine':
 
1995
 
1996
  orders = data.get('orders', {})
1997
  employees = data.get('employees', [])
 
1998
  fines = data.get('fines', [])
1999
 
2000
  return render_template_string(
2001
  SALARY_TEMPLATE,
2002
  orders_json=json.dumps(list(orders.values())),
2003
  employees_json=json.dumps(employees),
 
2004
  fines_json=json.dumps(fines),
2005
  employees=employees,
2006
+ current_date=get_current_date(),
2007
  currency_code=CURRENCY_CODE
2008
  )
2009
 
 
2032
  elif action == 'add_employee':
2033
  emp_name = request.form.get('employee_name', '').strip()
2034
  pin = request.form.get('pin', '0000').strip()
2035
+ try: daily_rate = float(request.form.get('daily_rate', 0))
2036
+ except: daily_rate = 0
2037
+ try: target_amount = float(request.form.get('target_amount', 0))
2038
+ except: target_amount = 0
2039
+ try: bonus_percentage = float(request.form.get('bonus_percentage', 0))
2040
+ except: bonus_percentage = 0
2041
 
2042
  if emp_name:
2043
  employees.append({
 
2057
  if e.get('id') == emp_id:
2058
  e['name'] = request.form.get('name', '').strip()
2059
  e['pin'] = request.form.get('pin', '0000').strip()
2060
+ try: e['daily_rate'] = float(request.form.get('daily_rate', 0))
2061
+ except: e['daily_rate'] = 0
2062
+ try: e['target_amount'] = float(request.form.get('target_amount', 0))
2063
+ except: e['target_amount'] = 0
2064
+ try: e['bonus_percentage'] = float(request.form.get('bonus_percentage', 0))
2065
+ except: e['bonus_percentage'] = 0
2066
  break
2067
  data['employees'] = employees
2068
  save_data(data)