Kgshop commited on
Commit
732628b
·
verified ·
1 Parent(s): 1ee12ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +123 -40
app.py CHANGED
@@ -63,7 +63,7 @@ def download_db_from_hf(specific_file=None, retries=3, delay=5):
63
  try:
64
  if file_name == DATA_FILE:
65
  with open(file_name, 'w', encoding='utf-8') as f:
66
- json.dump({'products': [], 'categories': [], 'orders': {}, 'employees': [], 'workdays': {}, 'settings': {
67
  'cafe_name': 'HongKong',
68
  'wa_shift1': '+77470623684',
69
  'wa_shift2': '+77470623684',
@@ -122,6 +122,7 @@ def load_data():
122
  'orders': {},
123
  'employees': [],
124
  'workdays': {},
 
125
  'settings': {
126
  'cafe_name': 'Sova',
127
  'wa_shift1': '+77470623684',
@@ -141,6 +142,7 @@ def load_data():
141
  if 'orders' not in data: data['orders'] = {}
142
  if 'employees' not in data: data['employees'] = []
143
  if 'workdays' not in data: data['workdays'] = {}
 
144
  if 'settings' not in data: data['settings'] = default_data['settings']
145
  if 'logo_version' not in data['settings']: data['settings']['logo_version'] = '1'
146
  except (FileNotFoundError, json.JSONDecodeError):
@@ -153,6 +155,7 @@ def load_data():
153
  if 'orders' not in data: data['orders'] = {}
154
  if 'employees' not in data: data['employees'] = []
155
  if 'workdays' not in data: data['workdays'] = {}
 
156
  if 'settings' not in data: data['settings'] = default_data['settings']
157
  if 'logo_version' not in data['settings']: data['settings']['logo_version'] = '1'
158
  except Exception:
@@ -177,9 +180,9 @@ def load_data():
177
 
178
  for emp in data['employees']:
179
  if 'pin' not in emp: emp['pin'] = '0000'
180
- if 'daily_rate' not in emp: emp['daily_rate'] = 0
181
- if 'target_amount' not in emp: emp['target_amount'] = 0
182
- if 'bonus_percentage' not in emp: emp['bonus_percentage'] = 0
183
 
184
  if not os.path.exists(DATA_FILE):
185
  try:
@@ -198,6 +201,7 @@ def save_data(data):
198
  if 'orders' not in data: data['orders'] = {}
199
  if 'employees' not in data: data['employees'] = []
200
  if 'workdays' not in data: data['workdays'] = {}
 
201
  if 'settings' not in data: data['settings'] = {
202
  'cafe_name': 'Sova',
203
  'wa_shift1': '',
@@ -1229,6 +1233,7 @@ SALARY_TEMPLATE = '''
1229
  const allOrders = {{ orders_json|safe }};
1230
  const workdays = {{ workdays_json|safe }};
1231
  const employees = {{ employees_json|safe }};
 
1232
  const currencyCode = '{{ currency_code }}';
1233
 
1234
  function getLocalDateStr(d) {
@@ -1249,7 +1254,7 @@ SALARY_TEMPLATE = '''
1249
  const end = document.getElementById('salEnd').value;
1250
  if (!start || !end) return;
1251
 
1252
- let html = '<tr><th>Сотрудник</th><th>Дней отработано</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Итого ЗП</th></tr>';
1253
 
1254
  let empSales = {};
1255
  allOrders.forEach(o => {
@@ -1264,7 +1269,7 @@ SALARY_TEMPLATE = '''
1264
  employees.forEach(emp => {
1265
  let daysWorked = 0;
1266
  let fixedSal = 0;
1267
- let bonusSal = 0;
1268
 
1269
  let currDate = new Date(start);
1270
  const endDate = new Date(end);
@@ -1277,32 +1282,44 @@ SALARY_TEMPLATE = '''
1277
 
1278
  if (worked || dailySales > 0) {
1279
  daysWorked++;
1280
- fixedSal += parseFloat(emp.daily_rate || 0);
1281
-
1282
- const target = parseFloat(emp.target_amount || 0);
1283
- const pct = parseFloat(emp.bonus_percentage || 0);
1284
-
1285
- if (pct > 0 && dailySales >= target) {
1286
- bonusSal += dailySales * (pct / 100);
1287
- }
1288
  }
 
1289
  currDate.setDate(currDate.getDate() + 1);
1290
  }
1291
 
1292
- const total = fixedSal + bonusSal;
1293
- if (daysWorked > 0 || total > 0) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1294
  html += `<tr>
1295
  <td>${emp.name}</td>
1296
  <td>${daysWorked}</td>
1297
  <td>${fixedSal.toFixed(2)} ${currencyCode}</td>
1298
  <td>${bonusSal.toFixed(2)} ${currencyCode}</td>
 
1299
  <td style="font-weight:bold; color:var(--success);">${total.toFixed(2)} ${currencyCode}</td>
1300
  </tr>`;
1301
  }
1302
  });
1303
 
1304
- if (html === '<tr><th>Сотрудник</th><th>Дней отработано</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Итого ЗП</th></tr>') {
1305
- html += '<tr><td colspan="5">За выбранный период данных нет</td></tr>';
1306
  }
1307
  document.getElementById('tableSalary').innerHTML = html;
1308
  }
@@ -1387,6 +1404,10 @@ ADMIN_TEMPLATE = '''
1387
  .orders-table th { padding: 12px; background: #1a1a1a; border-bottom: 2px solid var(--border); color: var(--primary); font-size: 0.85rem; text-transform: uppercase; }
1388
  .orders-table td { padding: 12px; border-bottom: 1px solid var(--border); vertical-align: middle; color: var(--text); }
1389
  .orders-table tr:hover { background: #1a1a1a; }
 
 
 
 
1390
 
1391
  @media (max-width: 600px) {
1392
  .header-panel { flex-direction: column; align-items: stretch; text-align: center; }
@@ -1455,9 +1476,9 @@ ADMIN_TEMPLATE = '''
1455
  <input type="hidden" name="action" value="add_employee">
1456
  <div style="flex:1; min-width:150px;"><label>Имя</label><input type="text" name="employee_name" required autocomplete="off"></div>
1457
  <div style="flex:1; min-width:100px;"><label>PIN (4 цифры)</label><input type="text" name="pin" pattern="\\d{4}" required autocomplete="off"></div>
1458
- <div style="flex:1; min-width:100px;"><label>Ставка за день</label><input type="number" name="daily_rate" value="0" required></div>
1459
- <div style="flex:1; min-width:100px;"><label>Цель (n сумма)</label><input type="number" name="target_amount" value="0" required></div>
1460
- <div style="flex:1; min-width:100px;"><label>Бонус (%)</label><input type="number" name="bonus_percentage" value="0" step="0.1" required></div>
1461
  <button type="submit" class="btn btn-dark" style="width:100%; margin-top:10px;"><i class="fas fa-plus"></i> Добавить</button>
1462
  </form>
1463
  </div>
@@ -1476,10 +1497,45 @@ ADMIN_TEMPLATE = '''
1476
  <div style="flex:1; min-width:100px;"><label>Бонус %</label><input type="number" name="bonus_percentage" value="{{ emp.bonus_percentage }}" step="0.1" required></div>
1477
  <button type="submit" class="btn btn-primary" title="Со��ранить"><i class="fas fa-save"></i></button>
1478
  </form>
1479
- <form method="POST" style="margin-top:10px; text-align:right;" onsubmit="return confirm('Удалить сотрудника?');">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1480
  <input type="hidden" name="action" value="delete_employee">
1481
  <input type="hidden" name="employee_id" value="{{ emp.id }}">
1482
- <button type="submit" class="btn btn-danger"><i class="fas fa-trash"></i> Удалить</button>
1483
  </form>
1484
  </div>
1485
  {% endfor %}
@@ -1505,10 +1561,6 @@ ADMIN_TEMPLATE = '''
1505
  <th>Действия</th>
1506
  </tr>
1507
  {% for order in orders.values()|sort(attribute='created_at', reverse=True) %}
1508
- {% set raw_total = 0 %}
1509
- {% for item in order.cart %}
1510
- {% set raw_total = raw_total + (item.price|float * item.quantity|int) %}
1511
- {% endfor %}
1512
  <tr>
1513
  <td>
1514
  <a href="/order/{{ order.id }}" target="_blank" style="color:var(--info); font-weight:bold; text-decoration:none;">{{ order.id }}</a><br>
@@ -1518,7 +1570,7 @@ ADMIN_TEMPLATE = '''
1518
  Столик: {{ order.table_number }}<br>
1519
  Сотрудник: {{ order.employee_name|default('Не указан') }}
1520
  </td>
1521
- <td style="font-weight:600;">{{ raw_total }} {{ currency_code }}</td>
1522
  <td>
1523
  <span style="text-transform:uppercase; font-size:0.8rem;">
1524
  {% if order.payment_method == 'cash' %}Наличка
@@ -1900,12 +1952,14 @@ def admin_salary():
1900
  orders = data.get('orders', {})
1901
  employees = data.get('employees', [])
1902
  workdays = data.get('workdays', {})
 
1903
 
1904
  return render_template_string(
1905
  SALARY_TEMPLATE,
1906
  orders_json=json.dumps(list(orders.values())),
1907
  employees_json=json.dumps(employees),
1908
  workdays_json=json.dumps(workdays),
 
1909
  currency_code=CURRENCY_CODE
1910
  )
1911
 
@@ -1916,6 +1970,7 @@ def admin():
1916
  categories = data.get('categories', [])
1917
  orders = data.get('orders', {})
1918
  employees = data.get('employees', [])
 
1919
  settings = data.get('settings', {})
1920
 
1921
  if request.method == 'POST':
@@ -1934,12 +1989,12 @@ def admin():
1934
  elif action == 'add_employee':
1935
  emp_name = request.form.get('employee_name', '').strip()
1936
  pin = request.form.get('pin', '0000').strip()
1937
- try: daily_rate = float(request.form.get('daily_rate', 0))
1938
- except: daily_rate = 0
1939
- try: target_amount = float(request.form.get('target_amount', 0))
1940
- except: target_amount = 0
1941
- try: bonus_percentage = float(request.form.get('bonus_percentage', 0))
1942
- except: bonus_percentage = 0
1943
 
1944
  if emp_name:
1945
  employees.append({
@@ -1959,12 +2014,12 @@ def admin():
1959
  if e.get('id') == emp_id:
1960
  e['name'] = request.form.get('name', '').strip()
1961
  e['pin'] = request.form.get('pin', '0000').strip()
1962
- try: e['daily_rate'] = float(request.form.get('daily_rate', 0))
1963
- except: e['daily_rate'] = 0
1964
- try: e['target_amount'] = float(request.form.get('target_amount', 0))
1965
- except: e['target_amount'] = 0
1966
- try: e['bonus_percentage'] = float(request.form.get('bonus_percentage', 0))
1967
- except: e['bonus_percentage'] = 0
1968
  break
1969
  data['employees'] = employees
1970
  save_data(data)
@@ -1974,6 +2029,33 @@ def admin():
1974
  data['employees'] = [e for e in employees if e.get('id') != emp_id]
1975
  save_data(data)
1976
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1977
  elif action == 'delete_order':
1978
  order_id = request.form.get('order_id')
1979
  if order_id in orders:
@@ -2118,6 +2200,7 @@ def admin():
2118
  categories=categories,
2119
  orders=orders,
2120
  employees=employees,
 
2121
  repo_id=REPO_ID,
2122
  currency_code=CURRENCY_CODE,
2123
  settings=settings
 
63
  try:
64
  if file_name == DATA_FILE:
65
  with open(file_name, 'w', encoding='utf-8') as f:
66
+ json.dump({'products': [], 'categories': [], 'orders': {}, 'employees': [], 'workdays': {}, 'fines': {}, 'settings': {
67
  'cafe_name': 'HongKong',
68
  'wa_shift1': '+77470623684',
69
  'wa_shift2': '+77470623684',
 
122
  'orders': {},
123
  'employees': [],
124
  'workdays': {},
125
+ 'fines': {},
126
  'settings': {
127
  'cafe_name': 'Sova',
128
  'wa_shift1': '+77470623684',
 
142
  if 'orders' not in data: data['orders'] = {}
143
  if 'employees' not in data: data['employees'] = []
144
  if 'workdays' not in data: data['workdays'] = {}
145
+ if 'fines' not in data: data['fines'] = {}
146
  if 'settings' not in data: data['settings'] = default_data['settings']
147
  if 'logo_version' not in data['settings']: data['settings']['logo_version'] = '1'
148
  except (FileNotFoundError, json.JSONDecodeError):
 
155
  if 'orders' not in data: data['orders'] = {}
156
  if 'employees' not in data: data['employees'] = []
157
  if 'workdays' not in data: data['workdays'] = {}
158
+ if 'fines' not in data: data['fines'] = {}
159
  if 'settings' not in data: data['settings'] = default_data['settings']
160
  if 'logo_version' not in data['settings']: data['settings']['logo_version'] = '1'
161
  except Exception:
 
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:
 
201
  if 'orders' not in data: data['orders'] = {}
202
  if 'employees' not in data: data['employees'] = []
203
  if 'workdays' not in data: data['workdays'] = {}
204
+ if 'fines' not in data: data['fines'] = {}
205
  if 'settings' not in data: data['settings'] = {
206
  'cafe_name': 'Sova',
207
  'wa_shift1': '',
 
1233
  const allOrders = {{ orders_json|safe }};
1234
  const workdays = {{ workdays_json|safe }};
1235
  const employees = {{ employees_json|safe }};
1236
+ const fines = {{ fines_json|safe }};
1237
  const currencyCode = '{{ currency_code }}';
1238
 
1239
  function getLocalDateStr(d) {
 
1254
  const end = document.getElementById('salEnd').value;
1255
  if (!start || !end) return;
1256
 
1257
+ let html = '<tr><th>Сотрудник</th><th>Дней отработано</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Штрафы</th><th>Итого ЗП</th></tr>';
1258
 
1259
  let empSales = {};
1260
  allOrders.forEach(o => {
 
1269
  employees.forEach(emp => {
1270
  let daysWorked = 0;
1271
  let fixedSal = 0;
1272
+ let totalSales = 0;
1273
 
1274
  let currDate = new Date(start);
1275
  const endDate = new Date(end);
 
1282
 
1283
  if (worked || dailySales > 0) {
1284
  daysWorked++;
1285
+ fixedSal += parseFloat(emp.daily_rate || 230000);
 
 
 
 
 
 
 
1286
  }
1287
+ totalSales += dailySales;
1288
  currDate.setDate(currDate.getDate() + 1);
1289
  }
1290
 
1291
+ let bonusSal = 0;
1292
+ const target = parseFloat(emp.target_amount || 1500000);
1293
+ const pct = parseFloat(emp.bonus_percentage || 10);
1294
+
1295
+ if (pct > 0 && totalSales > target) {
1296
+ bonusSal = (totalSales - target) * (pct / 100);
1297
+ }
1298
+
1299
+ let totalFines = 0;
1300
+ if (fines[emp.id]) {
1301
+ fines[emp.id].forEach(f => {
1302
+ if (f.date >= start && f.date <= end) {
1303
+ totalFines += parseFloat(f.amount || 0);
1304
+ }
1305
+ });
1306
+ }
1307
+
1308
+ const total = fixedSal + bonusSal - totalFines;
1309
+ if (daysWorked > 0 || total > 0 || totalFines > 0) {
1310
  html += `<tr>
1311
  <td>${emp.name}</td>
1312
  <td>${daysWorked}</td>
1313
  <td>${fixedSal.toFixed(2)} ${currencyCode}</td>
1314
  <td>${bonusSal.toFixed(2)} ${currencyCode}</td>
1315
+ <td style="color:var(--danger);">${totalFines.toFixed(2)} ${currencyCode}</td>
1316
  <td style="font-weight:bold; color:var(--success);">${total.toFixed(2)} ${currencyCode}</td>
1317
  </tr>`;
1318
  }
1319
  });
1320
 
1321
+ if (html === '<tr><th>Сотрудник</th><th>Дней отработано</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Штрафы</th><th>Итого ЗП</th></tr>') {
1322
+ html += '<tr><td colspan="6">За выбранный период данных нет</td></tr>';
1323
  }
1324
  document.getElementById('tableSalary').innerHTML = html;
1325
  }
 
1404
  .orders-table th { padding: 12px; background: #1a1a1a; border-bottom: 2px solid var(--border); color: var(--primary); font-size: 0.85rem; text-transform: uppercase; }
1405
  .orders-table td { padding: 12px; border-bottom: 1px solid var(--border); vertical-align: middle; color: var(--text); }
1406
  .orders-table tr:hover { background: #1a1a1a; }
1407
+
1408
+ .rep-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; color: var(--text); }
1409
+ .rep-table th, .rep-table td { padding: 8px 5px; border-bottom: 1px solid var(--border); text-align: left; }
1410
+ .rep-table th { color: var(--text-muted); font-weight: normal; }
1411
 
1412
  @media (max-width: 600px) {
1413
  .header-panel { flex-direction: column; align-items: stretch; text-align: center; }
 
1476
  <input type="hidden" name="action" value="add_employee">
1477
  <div style="flex:1; min-width:150px;"><label>Имя</label><input type="text" name="employee_name" required autocomplete="off"></div>
1478
  <div style="flex:1; min-width:100px;"><label>PIN (4 цифры)</label><input type="text" name="pin" pattern="\\d{4}" required autocomplete="off"></div>
1479
+ <div style="flex:1; min-width:100px;"><label>Ставка за день</label><input type="number" name="daily_rate" value="230000" required></div>
1480
+ <div style="flex:1; min-width:100px;"><label>Цель (n сумма)</label><input type="number" name="target_amount" value="1500000" required></div>
1481
+ <div style="flex:1; min-width:100px;"><label>Бонус (%)</label><input type="number" name="bonus_percentage" value="10" step="0.1" required></div>
1482
  <button type="submit" class="btn btn-dark" style="width:100%; margin-top:10px;"><i class="fas fa-plus"></i> Добавить</button>
1483
  </form>
1484
  </div>
 
1497
  <div style="flex:1; min-width:100px;"><label>Бонус %</label><input type="number" name="bonus_percentage" value="{{ emp.bonus_percentage }}" step="0.1" required></div>
1498
  <button type="submit" class="btn btn-primary" title="Со��ранить"><i class="fas fa-save"></i></button>
1499
  </form>
1500
+
1501
+ <div style="margin-top:15px; border-top:1px dashed var(--border); padding-top:15px;">
1502
+ <h4 style="margin:0 0 10px 0; color:var(--primary);">Штрафы</h4>
1503
+ <form method="POST" style="display:flex; flex-wrap:wrap; gap:10px; align-items:flex-end; margin-bottom:10px;">
1504
+ <input type="hidden" name="action" value="add_fine">
1505
+ <input type="hidden" name="employee_id" value="{{ emp.id }}">
1506
+ <div style="flex:1; min-width:120px;"><label>Дата</label><input type="date" name="fine_date" required></div>
1507
+ <div style="flex:1; min-width:100px;"><label>Сумма</label><input type="number" name="fine_amount" required></div>
1508
+ <div style="flex:2; min-width:150px;"><label>Причина</label><input type="text" name="fine_reason" required autocomplete="off"></div>
1509
+ <button type="submit" class="btn btn-danger" style="padding:12px 15px;"><i class="fas fa-plus"></i></button>
1510
+ </form>
1511
+ {% if fines.get(emp.id) %}
1512
+ <table class="rep-table" style="font-size:0.85rem;">
1513
+ <tr><th>Дата</th><th>Причина</th><th>Сумма</th><th>Удалить</th></tr>
1514
+ {% for f in fines.get(emp.id) %}
1515
+ <tr>
1516
+ <td>{{ f.date }}</td>
1517
+ <td>{{ f.reason }}</td>
1518
+ <td>{{ f.amount }} {{ currency_code }}</td>
1519
+ <td>
1520
+ <form method="POST" style="margin:0;" onsubmit="return confirm('Удалить штраф?');">
1521
+ <input type="hidden" name="action" value="delete_fine">
1522
+ <input type="hidden" name="employee_id" value="{{ emp.id }}">
1523
+ <input type="hidden" name="fine_id" value="{{ f.id }}">
1524
+ <button type="submit" class="btn btn-danger" style="padding:3px 8px; font-size:0.75rem;"><i class="fas fa-trash"></i></button>
1525
+ </form>
1526
+ </td>
1527
+ </tr>
1528
+ {% endfor %}
1529
+ </table>
1530
+ {% else %}
1531
+ <div style="font-size:0.85rem; color:var(--text-muted);">Нет штрафов</div>
1532
+ {% endif %}
1533
+ </div>
1534
+
1535
+ <form method="POST" style="margin-top:15px; text-align:right;" onsubmit="return confirm('Удалить сотрудника?');">
1536
  <input type="hidden" name="action" value="delete_employee">
1537
  <input type="hidden" name="employee_id" value="{{ emp.id }}">
1538
+ <button type="submit" class="btn btn-danger" style="margin-top: 10px;"><i class="fas fa-trash"></i> Удалить сотрудника</button>
1539
  </form>
1540
  </div>
1541
  {% endfor %}
 
1561
  <th>Действия</th>
1562
  </tr>
1563
  {% for order in orders.values()|sort(attribute='created_at', reverse=True) %}
 
 
 
 
1564
  <tr>
1565
  <td>
1566
  <a href="/order/{{ order.id }}" target="_blank" style="color:var(--info); font-weight:bold; text-decoration:none;">{{ order.id }}</a><br>
 
1570
  Столик: {{ order.table_number }}<br>
1571
  Сотрудник: {{ order.employee_name|default('Не указан') }}
1572
  </td>
1573
+ <td style="font-weight:600;">{{ order.total_price }} {{ currency_code }}</td>
1574
  <td>
1575
  <span style="text-transform:uppercase; font-size:0.8rem;">
1576
  {% if order.payment_method == 'cash' %}Наличка
 
1952
  orders = data.get('orders', {})
1953
  employees = data.get('employees', [])
1954
  workdays = data.get('workdays', {})
1955
+ fines = data.get('fines', {})
1956
 
1957
  return render_template_string(
1958
  SALARY_TEMPLATE,
1959
  orders_json=json.dumps(list(orders.values())),
1960
  employees_json=json.dumps(employees),
1961
  workdays_json=json.dumps(workdays),
1962
+ fines_json=json.dumps(fines),
1963
  currency_code=CURRENCY_CODE
1964
  )
1965
 
 
1970
  categories = data.get('categories', [])
1971
  orders = data.get('orders', {})
1972
  employees = data.get('employees', [])
1973
+ fines = data.get('fines', {})
1974
  settings = data.get('settings', {})
1975
 
1976
  if request.method == 'POST':
 
1989
  elif action == 'add_employee':
1990
  emp_name = request.form.get('employee_name', '').strip()
1991
  pin = request.form.get('pin', '0000').strip()
1992
+ try: daily_rate = float(request.form.get('daily_rate', 230000))
1993
+ except: daily_rate = 230000
1994
+ try: target_amount = float(request.form.get('target_amount', 1500000))
1995
+ except: target_amount = 1500000
1996
+ try: bonus_percentage = float(request.form.get('bonus_percentage', 10))
1997
+ except: bonus_percentage = 10
1998
 
1999
  if emp_name:
2000
  employees.append({
 
2014
  if e.get('id') == emp_id:
2015
  e['name'] = request.form.get('name', '').strip()
2016
  e['pin'] = request.form.get('pin', '0000').strip()
2017
+ try: e['daily_rate'] = float(request.form.get('daily_rate', 230000))
2018
+ except: e['daily_rate'] = 230000
2019
+ try: e['target_amount'] = float(request.form.get('target_amount', 1500000))
2020
+ except: e['target_amount'] = 1500000
2021
+ try: e['bonus_percentage'] = float(request.form.get('bonus_percentage', 10))
2022
+ except: e['bonus_percentage'] = 10
2023
  break
2024
  data['employees'] = employees
2025
  save_data(data)
 
2029
  data['employees'] = [e for e in employees if e.get('id') != emp_id]
2030
  save_data(data)
2031
 
2032
+ elif action == 'add_fine':
2033
+ emp_id = request.form.get('employee_id')
2034
+ fine_date = request.form.get('fine_date')
2035
+ try: fine_amount = float(request.form.get('fine_amount', 0))
2036
+ except: fine_amount = 0
2037
+ fine_reason = request.form.get('fine_reason', '').strip()
2038
+
2039
+ if emp_id and fine_date and fine_amount > 0:
2040
+ if 'fines' not in data:
2041
+ data['fines'] = {}
2042
+ if emp_id not in data['fines']:
2043
+ data['fines'][emp_id] = []
2044
+ data['fines'][emp_id].append({
2045
+ 'id': uuid4().hex,
2046
+ 'date': fine_date,
2047
+ 'amount': fine_amount,
2048
+ 'reason': fine_reason
2049
+ })
2050
+ save_data(data)
2051
+
2052
+ elif action == 'delete_fine':
2053
+ emp_id = request.form.get('employee_id')
2054
+ fine_id = request.form.get('fine_id')
2055
+ if 'fines' in data and emp_id in data['fines']:
2056
+ data['fines'][emp_id] = [f for f in data['fines'][emp_id] if f.get('id') != fine_id]
2057
+ save_data(data)
2058
+
2059
  elif action == 'delete_order':
2060
  order_id = request.form.get('order_id')
2061
  if order_id in orders:
 
2200
  categories=categories,
2201
  orders=orders,
2202
  employees=employees,
2203
+ fines=data.get('fines', {}),
2204
  repo_id=REPO_ID,
2205
  currency_code=CURRENCY_CODE,
2206
  settings=settings