Spaces:
Running
Running
Update app.py
Browse files
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'] =
|
| 184 |
-
if 'target_amount' not in emp: emp['target_amount'] =
|
| 185 |
-
if 'bonus_percentage' not in emp: emp['bonus_percentage'] =
|
| 186 |
|
| 187 |
if not os.path.exists(DATA_FILE):
|
| 188 |
try:
|
|
@@ -1171,7 +1171,7 @@ SALARY_TEMPLATE = '''
|
|
| 1171 |
<head>
|
| 1172 |
<meta charset="UTF-8">
|
| 1173 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
| 1174 |
-
<title>Отчет по ЗП
|
| 1175 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 1176 |
<style>
|
| 1177 |
:root { --primary: #D4AF37; --bg: #000000; --surface: #111111; --border: #333333; --danger: #ff4757; --success: #2ed573; --info: #D4AF37; --warning: #ffa502; --text: #ffffff; --text-muted: #888888; }
|
|
@@ -1184,15 +1184,15 @@ SALARY_TEMPLATE = '''
|
|
| 1184 |
.btn { padding: 12px 20px; border: none; border-radius: 10px; font-weight: 700; cursor: pointer; color: #000; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; font-size: 0.95rem; transition: opacity 0.2s; }
|
| 1185 |
.btn:active { opacity: 0.8; }
|
| 1186 |
.btn-success { background: var(--success); }
|
| 1187 |
-
.btn-danger { background: var(--danger); }
|
| 1188 |
.btn-dark { background: #1a1a1a; color: var(--primary); border: 1px solid var(--border); }
|
| 1189 |
|
| 1190 |
.report-section { background: var(--surface); padding: 20px; border-radius: 16px; border: 1px solid var(--border); margin-bottom: 20px; }
|
| 1191 |
|
| 1192 |
.form-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
| 1193 |
.form-row > * { flex: 1; min-width: 150px; }
|
| 1194 |
-
input[type="date"], input[type="
|
| 1195 |
-
input[type="date"]:focus, input[type="
|
| 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; }
|
|
@@ -1213,84 +1213,81 @@ SALARY_TEMPLATE = '''
|
|
| 1213 |
</div>
|
| 1214 |
|
| 1215 |
<div class="report-section">
|
| 1216 |
-
<
|
| 1217 |
-
|
| 1218 |
-
|
| 1219 |
-
|
| 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:120px;">
|
| 1228 |
-
<label>Дата</label>
|
| 1229 |
-
<input type="date" name="date" required id="fineDate">
|
| 1230 |
-
</div>
|
| 1231 |
-
<div style="flex:1; min-width:120px;">
|
| 1232 |
-
<label>Сумма</label>
|
| 1233 |
-
<input type="number" name="amount" required step="1" min="0">
|
| 1234 |
</div>
|
| 1235 |
-
<div
|
| 1236 |
-
<label>
|
| 1237 |
-
<input type="
|
| 1238 |
</div>
|
| 1239 |
-
<button
|
| 1240 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1241 |
|
| 1242 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1243 |
<div style="overflow-x:auto;">
|
| 1244 |
<table class="rep-table">
|
| 1245 |
-
<tr><th>Сотрудник</th><th>
|
| 1246 |
-
{% for fine in fines %}
|
| 1247 |
<tr>
|
|
|
|
| 1248 |
<td>
|
| 1249 |
{% for emp in employees %}
|
| 1250 |
{% if emp.id == fine.employee_id %}{{ emp.name }}{% endif %}
|
| 1251 |
{% endfor %}
|
| 1252 |
</td>
|
| 1253 |
-
<td>{{ fine.date }}</td>
|
| 1254 |
<td>{{ fine.reason }}</td>
|
| 1255 |
-
<td style="color:var(--danger); font-weight:bold;">
|
| 1256 |
<td>
|
| 1257 |
<form method="POST" style="margin:0;" onsubmit="return confirm('Удалить штраф?');">
|
| 1258 |
<input type="hidden" name="action" value="delete_fine">
|
| 1259 |
<input type="hidden" name="fine_id" value="{{ fine.id }}">
|
| 1260 |
-
<button type="submit" class="btn btn-dark" style="padding:
|
| 1261 |
</form>
|
| 1262 |
</td>
|
| 1263 |
</tr>
|
| 1264 |
{% endfor %}
|
| 1265 |
-
{% if not fines %}
|
| 1266 |
-
<tr><td colspan="5" style="text-align:center; padding:15px; color:var(--text-muted);">Нет штрафов</td></tr>
|
| 1267 |
-
{% endif %}
|
| 1268 |
</table>
|
| 1269 |
</div>
|
| 1270 |
</div>
|
| 1271 |
-
|
| 1272 |
-
<div class="report-section">
|
| 1273 |
-
<h2 style="margin-top:0; color:var(--primary); font-size:1.2rem; margin-bottom: 15px;">Генерация Зарплаты</h2>
|
| 1274 |
-
<div class="form-row" style="margin-bottom: 20px; align-items: flex-end;">
|
| 1275 |
-
<div>
|
| 1276 |
-
<label>Дата начала</label>
|
| 1277 |
-
<input type="date" id="salStart">
|
| 1278 |
-
</div>
|
| 1279 |
-
<div>
|
| 1280 |
-
<label>Дата конца</label>
|
| 1281 |
-
<input type="date" id="salEnd">
|
| 1282 |
-
</div>
|
| 1283 |
-
<button class="btn btn-success" style="height: 44px; color:#000;" onclick="generateSalaryReport()"><i class="fas fa-calculator"></i> Посчитать ЗП</button>
|
| 1284 |
-
</div>
|
| 1285 |
-
<div style="overflow-x:auto;">
|
| 1286 |
-
<table class="rep-table" id="tableSalary"></table>
|
| 1287 |
-
</div>
|
| 1288 |
-
</div>
|
| 1289 |
</div>
|
| 1290 |
|
| 1291 |
<script>
|
| 1292 |
const allOrders = {{ orders_json|safe }};
|
| 1293 |
-
const workdays = {{ workdays_json|safe }};
|
| 1294 |
const employees = {{ employees_json|safe }};
|
| 1295 |
const fines = {{ fines_json|safe }};
|
| 1296 |
const currencyCode = '{{ currency_code }}';
|
|
@@ -1305,8 +1302,6 @@ SALARY_TEMPLATE = '''
|
|
| 1305 |
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
|
| 1306 |
document.getElementById('salStart').value = getLocalDateStr(firstDay);
|
| 1307 |
document.getElementById('salEnd').value = todayStr;
|
| 1308 |
-
const fineDateInput = document.getElementById('fineDate');
|
| 1309 |
-
if(fineDateInput) { fineDateInput.value = todayStr; }
|
| 1310 |
generateSalaryReport();
|
| 1311 |
}
|
| 1312 |
|
|
@@ -1315,7 +1310,7 @@ SALARY_TEMPLATE = '''
|
|
| 1315 |
const end = document.getElementById('salEnd').value;
|
| 1316 |
if (!start || !end) return;
|
| 1317 |
|
| 1318 |
-
let html = '<tr><th>Сотрудник</th><th>Дней
|
| 1319 |
|
| 1320 |
let empSales = {};
|
| 1321 |
allOrders.forEach(o => {
|
|
@@ -1326,12 +1321,19 @@ SALARY_TEMPLATE = '''
|
|
| 1326 |
empSales[o.employee_id][oDate] += o.total_price;
|
| 1327 |
}
|
| 1328 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1329 |
|
| 1330 |
employees.forEach(emp => {
|
| 1331 |
let daysWorked = 0;
|
| 1332 |
let fixedSal = 0;
|
| 1333 |
let bonusSal = 0;
|
| 1334 |
-
let totalFines = 0;
|
| 1335 |
|
| 1336 |
let currDate = new Date(start);
|
| 1337 |
const endDate = new Date(end);
|
|
@@ -1355,12 +1357,7 @@ SALARY_TEMPLATE = '''
|
|
| 1355 |
currDate.setDate(currDate.getDate() + 1);
|
| 1356 |
}
|
| 1357 |
|
| 1358 |
-
|
| 1359 |
-
if (f.employee_id === emp.id && f.date >= start && f.date <= end) {
|
| 1360 |
-
totalFines += parseFloat(f.amount);
|
| 1361 |
-
}
|
| 1362 |
-
});
|
| 1363 |
-
|
| 1364 |
const total = fixedSal + bonusSal - totalFines;
|
| 1365 |
if (daysWorked > 0 || total > 0 || totalFines > 0) {
|
| 1366 |
html += `<tr>
|
|
@@ -1368,13 +1365,13 @@ SALARY_TEMPLATE = '''
|
|
| 1368 |
<td>${daysWorked}</td>
|
| 1369 |
<td>${fixedSal.toFixed(2)} ${currencyCode}</td>
|
| 1370 |
<td>${bonusSal.toFixed(2)} ${currencyCode}</td>
|
| 1371 |
-
<td style="color:var(--danger);">
|
| 1372 |
<td style="font-weight:bold; color:var(--success);">${total.toFixed(2)} ${currencyCode}</td>
|
| 1373 |
</tr>`;
|
| 1374 |
}
|
| 1375 |
});
|
| 1376 |
|
| 1377 |
-
if (html === '<tr><th>Сотрудник</th><th>Дней
|
| 1378 |
html += '<tr><td colspan="6">За выбранный период данных нет</td></tr>';
|
| 1379 |
}
|
| 1380 |
document.getElementById('tableSalary').innerHTML = html;
|
|
@@ -1970,16 +1967,14 @@ def admin_salary():
|
|
| 1970 |
if request.method == 'POST':
|
| 1971 |
action = request.form.get('action')
|
| 1972 |
if action == 'add_fine':
|
| 1973 |
-
|
| 1974 |
-
|
| 1975 |
-
|
| 1976 |
-
reason = request.form.get('reason', '')
|
| 1977 |
-
data.setdefault('fines', []).append({
|
| 1978 |
'id': uuid4().hex,
|
| 1979 |
-
'employee_id':
|
| 1980 |
-
'date':
|
| 1981 |
-
'amount': amount,
|
| 1982 |
-
'reason': reason
|
| 1983 |
})
|
| 1984 |
save_data(data)
|
| 1985 |
return redirect(url_for('admin_salary'))
|
|
@@ -1991,19 +1986,15 @@ def admin_salary():
|
|
| 1991 |
|
| 1992 |
orders = data.get('orders', {})
|
| 1993 |
employees = data.get('employees', [])
|
| 1994 |
-
workdays = data.get('workdays', {})
|
| 1995 |
fines = data.get('fines', [])
|
| 1996 |
|
| 1997 |
-
fines = sorted(fines, key=lambda x: x.get('date', ''), reverse=True)
|
| 1998 |
-
|
| 1999 |
return render_template_string(
|
| 2000 |
SALARY_TEMPLATE,
|
| 2001 |
orders_json=json.dumps(list(orders.values())),
|
|
|
|
| 2002 |
employees_json=json.dumps(employees),
|
| 2003 |
-
workdays_json=json.dumps(workdays),
|
| 2004 |
-
fines_json=json.dumps(fines),
|
| 2005 |
fines=fines,
|
| 2006 |
-
|
| 2007 |
currency_code=CURRENCY_CODE
|
| 2008 |
)
|
| 2009 |
|
|
@@ -2033,11 +2024,11 @@ def admin():
|
|
| 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 =
|
| 2037 |
try: target_amount = float(request.form.get('target_amount', 0))
|
| 2038 |
-
except: target_amount =
|
| 2039 |
try: bonus_percentage = float(request.form.get('bonus_percentage', 0))
|
| 2040 |
-
except: bonus_percentage =
|
| 2041 |
|
| 2042 |
if emp_name:
|
| 2043 |
employees.append({
|
|
@@ -2058,11 +2049,11 @@ def admin():
|
|
| 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'] =
|
| 2062 |
try: e['target_amount'] = float(request.form.get('target_amount', 0))
|
| 2063 |
-
except: e['target_amount'] =
|
| 2064 |
try: e['bonus_percentage'] = float(request.form.get('bonus_percentage', 0))
|
| 2065 |
-
except: e['bonus_percentage'] =
|
| 2066 |
break
|
| 2067 |
data['employees'] = employees
|
| 2068 |
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:
|
|
|
|
| 1171 |
<head>
|
| 1172 |
<meta charset="UTF-8">
|
| 1173 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
| 1174 |
+
<title>Отчет по ЗП</title>
|
| 1175 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 1176 |
<style>
|
| 1177 |
:root { --primary: #D4AF37; --bg: #000000; --surface: #111111; --border: #333333; --danger: #ff4757; --success: #2ed573; --info: #D4AF37; --warning: #ffa502; --text: #ffffff; --text-muted: #888888; }
|
|
|
|
| 1184 |
.btn { padding: 12px 20px; border: none; border-radius: 10px; font-weight: 700; cursor: pointer; color: #000; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; font-size: 0.95rem; transition: opacity 0.2s; }
|
| 1185 |
.btn:active { opacity: 0.8; }
|
| 1186 |
.btn-success { background: var(--success); }
|
| 1187 |
+
.btn-danger { background: var(--danger); color: #fff; }
|
| 1188 |
.btn-dark { background: #1a1a1a; color: var(--primary); border: 1px solid var(--border); }
|
| 1189 |
|
| 1190 |
.report-section { background: var(--surface); padding: 20px; border-radius: 16px; border: 1px solid var(--border); margin-bottom: 20px; }
|
| 1191 |
|
| 1192 |
.form-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
| 1193 |
.form-row > * { flex: 1; min-width: 150px; }
|
| 1194 |
+
input[type="date"], input[type="text"], input[type="number"], select { width: 100%; padding: 12px 15px; border: 1px solid var(--border); border-radius: 10px; font-size: 0.95rem; outline: none; transition: border-color 0.2s; background: #1a1a1a; color: var(--text); }
|
| 1195 |
+
input[type="date"]:focus, input[type="text"]:focus, input[type="number"]:focus, select:focus { border-color: var(--primary); background: #222; }
|
| 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; }
|
|
|
|
| 1213 |
</div>
|
| 1214 |
|
| 1215 |
<div class="report-section">
|
| 1216 |
+
<div class="form-row" style="margin-bottom: 20px; align-items: flex-end;">
|
| 1217 |
+
<div>
|
| 1218 |
+
<label>Дата начала</label>
|
| 1219 |
+
<input type="date" id="salStart">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1220 |
</div>
|
| 1221 |
+
<div>
|
| 1222 |
+
<label>Дата конца</label>
|
| 1223 |
+
<input type="date" id="salEnd">
|
| 1224 |
</div>
|
| 1225 |
+
<button class="btn btn-success" style="height: 44px; color:#000;" onclick="generateSalaryReport()"><i class="fas fa-calculator"></i> Посчитать ЗП</button>
|
| 1226 |
+
</div>
|
| 1227 |
+
<div style="overflow-x:auto;">
|
| 1228 |
+
<table class="rep-table" id="tableSalary"></table>
|
| 1229 |
+
</div>
|
| 1230 |
+
</div>
|
| 1231 |
+
|
| 1232 |
+
<div class="report-section" style="margin-top: 20px;">
|
| 1233 |
+
<h3 style="color:var(--primary); margin-top:0; border-bottom: 1px dashed var(--border); padding-bottom: 10px; margin-bottom: 20px;">Управление штрафами</h3>
|
| 1234 |
|
| 1235 |
+
<div style="background:#1a1a1a; border:1px solid var(--border); padding:15px; border-radius:12px; margin-bottom:20px;">
|
| 1236 |
+
<form method="POST" style="display:flex; flex-wrap:wrap; gap:10px; align-items:flex-end;">
|
| 1237 |
+
<input type="hidden" name="action" value="add_fine">
|
| 1238 |
+
<div style="flex:1; min-width:150px;">
|
| 1239 |
+
<label>Сотрудник</label>
|
| 1240 |
+
<select name="employee_id" required>
|
| 1241 |
+
{% for emp in employees %}
|
| 1242 |
+
<option value="{{ emp.id }}">{{ emp.name }}</option>
|
| 1243 |
+
{% endfor %}
|
| 1244 |
+
</select>
|
| 1245 |
+
</div>
|
| 1246 |
+
<div style="flex:1; min-width:150px;">
|
| 1247 |
+
<label>Дата</label>
|
| 1248 |
+
<input type="date" name="date" required>
|
| 1249 |
+
</div>
|
| 1250 |
+
<div style="flex:1; min-width:150px;">
|
| 1251 |
+
<label>Сумма</label>
|
| 1252 |
+
<input type="number" name="amount" required min="0" step="0.01">
|
| 1253 |
+
</div>
|
| 1254 |
+
<div style="flex:2; min-width:200px;">
|
| 1255 |
+
<label>Причина</label>
|
| 1256 |
+
<input type="text" name="reason" required placeholder="Причина штрафа">
|
| 1257 |
+
</div>
|
| 1258 |
+
<button type="submit" class="btn btn-danger" style="height: 44px; white-space: nowrap;"><i class="fas fa-plus"></i> Добавить штраф</button>
|
| 1259 |
+
</form>
|
| 1260 |
+
</div>
|
| 1261 |
+
|
| 1262 |
<div style="overflow-x:auto;">
|
| 1263 |
<table class="rep-table">
|
| 1264 |
+
<tr><th>Дата</th><th>Сотрудник</th><th>Причина</th><th>Сумма</th><th>Действие</th></tr>
|
| 1265 |
+
{% for fine in fines|sort(attribute='date', reverse=True) %}
|
| 1266 |
<tr>
|
| 1267 |
+
<td>{{ fine.date }}</td>
|
| 1268 |
<td>
|
| 1269 |
{% for emp in employees %}
|
| 1270 |
{% if emp.id == fine.employee_id %}{{ emp.name }}{% endif %}
|
| 1271 |
{% endfor %}
|
| 1272 |
</td>
|
|
|
|
| 1273 |
<td>{{ fine.reason }}</td>
|
| 1274 |
+
<td style="color:var(--danger); font-weight:bold;">{{ fine.amount }} {{ currency_code }}</td>
|
| 1275 |
<td>
|
| 1276 |
<form method="POST" style="margin:0;" onsubmit="return confirm('Удалить штраф?');">
|
| 1277 |
<input type="hidden" name="action" value="delete_fine">
|
| 1278 |
<input type="hidden" name="fine_id" value="{{ fine.id }}">
|
| 1279 |
+
<button type="submit" class="btn btn-dark" style="padding: 6px 12px;"><i class="fas fa-trash" style="color:var(--danger);"></i></button>
|
| 1280 |
</form>
|
| 1281 |
</td>
|
| 1282 |
</tr>
|
| 1283 |
{% endfor %}
|
|
|
|
|
|
|
|
|
|
| 1284 |
</table>
|
| 1285 |
</div>
|
| 1286 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1287 |
</div>
|
| 1288 |
|
| 1289 |
<script>
|
| 1290 |
const allOrders = {{ orders_json|safe }};
|
|
|
|
| 1291 |
const employees = {{ employees_json|safe }};
|
| 1292 |
const fines = {{ fines_json|safe }};
|
| 1293 |
const currencyCode = '{{ currency_code }}';
|
|
|
|
| 1302 |
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
|
| 1303 |
document.getElementById('salStart').value = getLocalDateStr(firstDay);
|
| 1304 |
document.getElementById('salEnd').value = todayStr;
|
|
|
|
|
|
|
| 1305 |
generateSalaryReport();
|
| 1306 |
}
|
| 1307 |
|
|
|
|
| 1310 |
const end = document.getElementById('salEnd').value;
|
| 1311 |
if (!start || !end) return;
|
| 1312 |
|
| 1313 |
+
let html = '<tr><th>Сотрудник</th><th>Дней с продажами</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Штрафы</th><th>Итого ЗП</th></tr>';
|
| 1314 |
|
| 1315 |
let empSales = {};
|
| 1316 |
allOrders.forEach(o => {
|
|
|
|
| 1321 |
empSales[o.employee_id][oDate] += o.total_price;
|
| 1322 |
}
|
| 1323 |
});
|
| 1324 |
+
|
| 1325 |
+
let empFines = {};
|
| 1326 |
+
fines.forEach(f => {
|
| 1327 |
+
if (f.date >= start && f.date <= end) {
|
| 1328 |
+
if (!empFines[f.employee_id]) empFines[f.employee_id] = 0;
|
| 1329 |
+
empFines[f.employee_id] += parseFloat(f.amount);
|
| 1330 |
+
}
|
| 1331 |
+
});
|
| 1332 |
|
| 1333 |
employees.forEach(emp => {
|
| 1334 |
let daysWorked = 0;
|
| 1335 |
let fixedSal = 0;
|
| 1336 |
let bonusSal = 0;
|
|
|
|
| 1337 |
|
| 1338 |
let currDate = new Date(start);
|
| 1339 |
const endDate = new Date(end);
|
|
|
|
| 1357 |
currDate.setDate(currDate.getDate() + 1);
|
| 1358 |
}
|
| 1359 |
|
| 1360 |
+
const totalFines = empFines[emp.id] || 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1361 |
const total = fixedSal + bonusSal - totalFines;
|
| 1362 |
if (daysWorked > 0 || total > 0 || totalFines > 0) {
|
| 1363 |
html += `<tr>
|
|
|
|
| 1365 |
<td>${daysWorked}</td>
|
| 1366 |
<td>${fixedSal.toFixed(2)} ${currencyCode}</td>
|
| 1367 |
<td>${bonusSal.toFixed(2)} ${currencyCode}</td>
|
| 1368 |
+
<td style="color:var(--danger);">${totalFines.toFixed(2)} ${currencyCode}</td>
|
| 1369 |
<td style="font-weight:bold; color:var(--success);">${total.toFixed(2)} ${currencyCode}</td>
|
| 1370 |
</tr>`;
|
| 1371 |
}
|
| 1372 |
});
|
| 1373 |
|
| 1374 |
+
if (html === '<tr><th>Сотрудник</th><th>Дней с продажами</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Штрафы</th><th>Итого ЗП</th></tr>') {
|
| 1375 |
html += '<tr><td colspan="6">За выбранный период данных нет</td></tr>';
|
| 1376 |
}
|
| 1377 |
document.getElementById('tableSalary').innerHTML = html;
|
|
|
|
| 1967 |
if request.method == 'POST':
|
| 1968 |
action = request.form.get('action')
|
| 1969 |
if action == 'add_fine':
|
| 1970 |
+
if 'fines' not in data:
|
| 1971 |
+
data['fines'] = []
|
| 1972 |
+
data['fines'].append({
|
|
|
|
|
|
|
| 1973 |
'id': uuid4().hex,
|
| 1974 |
+
'employee_id': request.form.get('employee_id'),
|
| 1975 |
+
'date': request.form.get('date'),
|
| 1976 |
+
'amount': float(request.form.get('amount', 0)),
|
| 1977 |
+
'reason': request.form.get('reason', '')
|
| 1978 |
})
|
| 1979 |
save_data(data)
|
| 1980 |
return redirect(url_for('admin_salary'))
|
|
|
|
| 1986 |
|
| 1987 |
orders = data.get('orders', {})
|
| 1988 |
employees = data.get('employees', [])
|
|
|
|
| 1989 |
fines = data.get('fines', [])
|
| 1990 |
|
|
|
|
|
|
|
| 1991 |
return render_template_string(
|
| 1992 |
SALARY_TEMPLATE,
|
| 1993 |
orders_json=json.dumps(list(orders.values())),
|
| 1994 |
+
employees=employees,
|
| 1995 |
employees_json=json.dumps(employees),
|
|
|
|
|
|
|
| 1996 |
fines=fines,
|
| 1997 |
+
fines_json=json.dumps(fines),
|
| 1998 |
currency_code=CURRENCY_CODE
|
| 1999 |
)
|
| 2000 |
|
|
|
|
| 2024 |
emp_name = request.form.get('employee_name', '').strip()
|
| 2025 |
pin = request.form.get('pin', '0000').strip()
|
| 2026 |
try: daily_rate = float(request.form.get('daily_rate', 0))
|
| 2027 |
+
except: daily_rate = 0
|
| 2028 |
try: target_amount = float(request.form.get('target_amount', 0))
|
| 2029 |
+
except: target_amount = 0
|
| 2030 |
try: bonus_percentage = float(request.form.get('bonus_percentage', 0))
|
| 2031 |
+
except: bonus_percentage = 0
|
| 2032 |
|
| 2033 |
if emp_name:
|
| 2034 |
employees.append({
|
|
|
|
| 2049 |
e['name'] = request.form.get('name', '').strip()
|
| 2050 |
e['pin'] = request.form.get('pin', '0000').strip()
|
| 2051 |
try: e['daily_rate'] = float(request.form.get('daily_rate', 0))
|
| 2052 |
+
except: e['daily_rate'] = 0
|
| 2053 |
try: e['target_amount'] = float(request.form.get('target_amount', 0))
|
| 2054 |
+
except: e['target_amount'] = 0
|
| 2055 |
try: e['bonus_percentage'] = float(request.form.get('bonus_percentage', 0))
|
| 2056 |
+
except: e['bonus_percentage'] = 0
|
| 2057 |
break
|
| 2058 |
data['employees'] = employees
|
| 2059 |
save_data(data)
|