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:
|
|
@@ -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; }
|
|
@@ -1211,54 +1211,22 @@ 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 |
-
<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>
|
|
@@ -1271,12 +1239,12 @@ SALARY_TEMPLATE = '''
|
|
| 1271 |
{% endfor %}
|
| 1272 |
</td>
|
| 1273 |
<td>{{ fine.reason }}</td>
|
| 1274 |
-
<td
|
| 1275 |
<td>
|
| 1276 |
-
<form method="POST"
|
| 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:
|
| 1280 |
</form>
|
| 1281 |
</td>
|
| 1282 |
</tr>
|
|
@@ -1284,10 +1252,29 @@ SALARY_TEMPLATE = '''
|
|
| 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,6 +1289,7 @@ SALARY_TEMPLATE = '''
|
|
| 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,7 +1298,7 @@ SALARY_TEMPLATE = '''
|
|
| 1310 |
const end = document.getElementById('salEnd').value;
|
| 1311 |
if (!start || !end) return;
|
| 1312 |
|
| 1313 |
-
let html = '<tr><th>Сотрудник</th><th>Дней
|
| 1314 |
|
| 1315 |
let empSales = {};
|
| 1316 |
allOrders.forEach(o => {
|
|
@@ -1321,34 +1309,32 @@ SALARY_TEMPLATE = '''
|
|
| 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);
|
| 1340 |
|
| 1341 |
while(currDate <= endDate) {
|
| 1342 |
const dateStr = getLocalDateStr(currDate);
|
| 1343 |
-
|
| 1344 |
const dailySales = (empSales[emp.id] && empSales[emp.id][dateStr]) ? empSales[emp.id][dateStr] : 0;
|
| 1345 |
|
| 1346 |
if (dailySales > 0) {
|
| 1347 |
daysWorked++;
|
| 1348 |
-
fixedSal += parseFloat(emp.daily_rate || 0);
|
| 1349 |
|
| 1350 |
-
|
| 1351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1352 |
|
| 1353 |
if (pct > 0 && dailySales > target) {
|
| 1354 |
bonusSal += (dailySales - target) * (pct / 100);
|
|
@@ -1356,22 +1342,27 @@ SALARY_TEMPLATE = '''
|
|
| 1356 |
}
|
| 1357 |
currDate.setDate(currDate.getDate() + 1);
|
| 1358 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1359 |
|
| 1360 |
-
const
|
| 1361 |
-
|
| 1362 |
-
if (daysWorked > 0 || total > 0 || totalFines > 0) {
|
| 1363 |
html += `<tr>
|
| 1364 |
<td>${emp.name}</td>
|
| 1365 |
<td>${daysWorked}</td>
|
| 1366 |
<td>${fixedSal.toFixed(2)} ${currencyCode}</td>
|
| 1367 |
<td>${bonusSal.toFixed(2)} ${currencyCode}</td>
|
| 1368 |
-
<td style="color:var(--danger);">${
|
| 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>Дней
|
| 1375 |
html += '<tr><td colspan="6">За выбранный период данных нет</td></tr>';
|
| 1376 |
}
|
| 1377 |
document.getElementById('tableSalary').innerHTML = html;
|
|
@@ -1977,24 +1968,26 @@ def admin_salary():
|
|
| 1977 |
'reason': request.form.get('reason', '')
|
| 1978 |
})
|
| 1979 |
save_data(data)
|
| 1980 |
-
return redirect(url_for('admin_salary'))
|
| 1981 |
elif action == 'delete_fine':
|
| 1982 |
fine_id = request.form.get('fine_id')
|
| 1983 |
-
|
| 1984 |
-
|
| 1985 |
-
|
|
|
|
| 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 |
-
|
| 1997 |
fines_json=json.dumps(fines),
|
|
|
|
|
|
|
| 1998 |
currency_code=CURRENCY_CODE
|
| 1999 |
)
|
| 2000 |
|
|
@@ -2023,12 +2016,12 @@ def admin():
|
|
| 2023 |
elif action == 'add_employee':
|
| 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',
|
| 2027 |
-
except: daily_rate =
|
| 2028 |
-
try: target_amount = float(request.form.get('target_amount',
|
| 2029 |
-
except: target_amount =
|
| 2030 |
-
try: bonus_percentage = float(request.form.get('bonus_percentage',
|
| 2031 |
-
except: bonus_percentage =
|
| 2032 |
|
| 2033 |
if emp_name:
|
| 2034 |
employees.append({
|
|
@@ -2048,12 +2041,12 @@ def admin():
|
|
| 2048 |
if e.get('id') == emp_id:
|
| 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',
|
| 2052 |
-
except: e['daily_rate'] =
|
| 2053 |
-
try: e['target_amount'] = float(request.form.get('target_amount',
|
| 2054 |
-
except: e['target_amount'] =
|
| 2055 |
-
try: e['bonus_percentage'] = float(request.form.get('bonus_percentage',
|
| 2056 |
-
except: e['bonus_percentage'] =
|
| 2057 |
break
|
| 2058 |
data['employees'] = employees
|
| 2059 |
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'] = 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:
|
|
|
|
| 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="number"], input[type="text"], 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="number"]:focus, input[type="text"]: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; }
|
|
|
|
| 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>
|
|
|
|
| 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>
|
|
|
|
| 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>
|
| 1261 |
+
<input type="date" id="salStart">
|
| 1262 |
+
</div>
|
| 1263 |
+
<div>
|
| 1264 |
+
<label>Дата конца</label>
|
| 1265 |
+
<input type="date" id="salEnd">
|
| 1266 |
+
</div>
|
| 1267 |
+
<button class="btn btn-success" style="height: 44px; color:#000;" onclick="generateSalaryReport()"><i class="fas fa-calculator"></i> Посчитать ЗП</button>
|
| 1268 |
+
</div>
|
| 1269 |
+
<div style="overflow-x:auto;">
|
| 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 }}';
|
|
|
|
| 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 |
|
|
|
|
| 1298 |
const end = document.getElementById('salEnd').value;
|
| 1299 |
if (!start || !end) return;
|
| 1300 |
|
| 1301 |
+
let html = '<tr><th>Сотрудник</th><th>Дней отработано</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Штрафы</th><th>Итого ЗП</th></tr>';
|
| 1302 |
|
| 1303 |
let empSales = {};
|
| 1304 |
allOrders.forEach(o => {
|
|
|
|
| 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);
|
| 1321 |
|
| 1322 |
while(currDate <= endDate) {
|
| 1323 |
const dateStr = getLocalDateStr(currDate);
|
|
|
|
| 1324 |
const dailySales = (empSales[emp.id] && empSales[emp.id][dateStr]) ? empSales[emp.id][dateStr] : 0;
|
| 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 |
}
|
| 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 |
});
|
| 1364 |
|
| 1365 |
+
if (html === '<tr><th>Сотрудник</th><th>Дней отработано</th><th>Фикс. ЗП</th><th>Бонусы с продаж</th><th>Штрафы</th><th>Итого ЗП</th></tr>') {
|
| 1366 |
html += '<tr><td colspan="6">За выбранный период данных нет</td></tr>';
|
| 1367 |
}
|
| 1368 |
document.getElementById('tableSalary').innerHTML = html;
|
|
|
|
| 1968 |
'reason': request.form.get('reason', '')
|
| 1969 |
})
|
| 1970 |
save_data(data)
|
|
|
|
| 1971 |
elif action == 'delete_fine':
|
| 1972 |
fine_id = request.form.get('fine_id')
|
| 1973 |
+
if 'fines' in data:
|
| 1974 |
+
data['fines'] = [f for f in data['fines'] if f.get('id') != fine_id]
|
| 1975 |
+
save_data(data)
|
| 1976 |
+
return redirect(url_for('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 |
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 |
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)
|