Kgshop commited on
Commit
843d9aa
·
verified ·
1 Parent(s): 93a44e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -8
app.py CHANGED
@@ -186,6 +186,8 @@ def load_data():
186
  'system_mode': 'both',
187
  'hide_stock_online': False,
188
  'closed_catalog_enabled': False,
 
 
189
  'theme': 'light',
190
  'customer_fields': {
191
  'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False
@@ -226,6 +228,8 @@ def load_data():
226
  if 'system_mode' not in settings: settings['system_mode'] = 'both'; changed = True
227
  if 'hide_stock_online' not in settings: settings['hide_stock_online'] = False; changed = True
228
  if 'closed_catalog_enabled' not in settings: settings['closed_catalog_enabled'] = False; changed = True
 
 
229
  if 'theme' not in settings: settings['theme'] = 'light'; changed = True
230
  if 'customer_fields' not in settings:
231
  settings['customer_fields'] = {'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False}
@@ -263,6 +267,7 @@ def load_data():
263
  if 'staff_name' not in order: order['staff_name'] = ''; changed = True
264
  if 'assembled' not in order: order['assembled'] = {}; changed = True
265
  if 'global_discount' not in order: order['global_discount'] = 0; changed = True
 
266
  for item in order.get('cart', []):
267
  if 'discount' not in item: item['discount'] = 0; changed = True
268
  if 'category' not in item: item['category'] = 'Без категории'; changed = True
@@ -317,6 +322,8 @@ def get_env_data(env_id):
317
  'system_mode': 'both',
318
  'hide_stock_online': False,
319
  'closed_catalog_enabled': False,
 
 
320
  'theme': 'light',
321
  'customer_fields': {'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False},
322
  'socials': {
@@ -334,7 +341,10 @@ def save_env_data(env_id, env_data):
334
  all_data[env_id] = env_data
335
  save_data(all_data)
336
 
337
- def update_order_totals(order, business_type):
 
 
 
338
  total = 0
339
  global_discount = float(order.get('global_discount', 0))
340
  for i in order['cart']:
@@ -363,6 +373,14 @@ def update_order_totals(order, business_type):
363
  total += item_total
364
 
365
  total = max(0, total - global_discount)
 
 
 
 
 
 
 
 
366
  order['total_price'] = round(total, 2)
367
 
368
  def is_order_fully_assembled(order):
@@ -967,6 +985,8 @@ CATALOG_TEMPLATE = '''
967
  const hideStockOnline = {{ 'true' if settings.hide_stock_online else 'false' }};
968
  const businessType = '{{ settings.business_type }}';
969
  const cFields = {{ settings.customer_fields|tojson }};
 
 
970
 
971
  let cart = {};
972
  let currentGalleryPhotos =[];
@@ -1374,6 +1394,10 @@ CATALOG_TEMPLATE = '''
1374
 
1375
  total = total - globalDisc;
1376
  if(total < 0) total = 0;
 
 
 
 
1377
 
1378
  const cartBar = document.getElementById('cartBar');
1379
  if (total > 0 || Object.keys(cart).length > 0) {
@@ -1427,6 +1451,10 @@ CATALOG_TEMPLATE = '''
1427
  </div>
1428
  `;
1429
  }
 
 
 
 
1430
  }
1431
 
1432
  function openCartModal() {
@@ -1927,6 +1955,9 @@ ORDER_TEMPLATE = '''
1927
  {% if order.global_discount > 0 %}
1928
  <div style="color:#e17055; font-size:0.9rem; margin-bottom:5px;">Применена общая скидка: -{{ order.global_discount }} {{ currency_code }}</div>
1929
  {% endif %}
 
 
 
1930
  Итого:
1931
  </td>
1932
  <td>{{ order.total_price }} {{ currency_code }}</td>
@@ -2702,6 +2733,12 @@ ADMIN_TEMPLATE = '''
2702
  <label><input type="checkbox" name="closed_catalog_enabled" {% if settings.closed_catalog_enabled %}checked{% endif %}> Включить закрытый каталог (доступ только по паролю из раздела пользователей)</label>
2703
  {% endif %}
2704
 
 
 
 
 
 
 
2705
  <div style="font-weight: 600; margin-bottom: 5px; border-top: 1px solid var(--border); padding-top: 15px; margin-top: 10px;">Безопасность (Админ-панель):</div>
2706
  <div class="social-item" style="margin-bottom: 10px;">
2707
  <label style="width: auto; margin-right: 15px;"><input type="checkbox" name="admin_password_enabled" {% if settings.admin_password_enabled %}checked{% endif %}> Пароль на вход</label>
@@ -3538,7 +3575,7 @@ REPORTS_TEMPLATE = '''
3538
  .tabs { display: flex; gap: 10px; margin-bottom: 20px; border-bottom: 2px solid var(--border); padding-bottom: 10px; flex-wrap: wrap; }
3539
  .tab { padding: 10px 20px; cursor: pointer; font-weight: 600; color: #636e72; border-radius: 8px; transition: background 0.2s; }
3540
  .tab:hover { background: #e9ecef; }
3541
- .tab.active { background: var(--primary); color: #fff; }
3542
 
3543
  .tab-content { display: none; }
3544
  .tab-content.active { display: block; }
@@ -3615,6 +3652,15 @@ REPORTS_TEMPLATE = '''
3615
  <i class="fas fa-undo icon"></i>
3616
  </div>
3617
  </div>
 
 
 
 
 
 
 
 
 
3618
  </div>
3619
 
3620
  <div class="table-container">
@@ -3723,6 +3769,7 @@ REPORTS_TEMPLATE = '''
3723
  let totalRet = 0;
3724
  let totalItems = 0;
3725
  let ordersCount = 0;
 
3726
 
3727
  let staffSales = {};
3728
  let productSales = {};
@@ -3735,6 +3782,10 @@ REPORTS_TEMPLATE = '''
3735
  } else {
3736
  totalRev += o.total_price;
3737
  ordersCount++;
 
 
 
 
3738
 
3739
  const staff = o.staff_name || 'Онлайн (Без сотрудника)';
3740
  if(!staffSales[staff]) staffSales[staff] = { orders: 0, sum: 0 };
@@ -3772,6 +3823,11 @@ REPORTS_TEMPLATE = '''
3772
  document.getElementById('totalReturns').innerText = totalRet.toLocaleString() + ' {{ currency_code }}';
3773
  document.getElementById('totalItemsSold').innerText = totalItems.toLocaleString();
3774
 
 
 
 
 
 
3775
  let aov = ordersCount > 0 ? (totalRev / ordersCount) : 0;
3776
  document.getElementById('avgOrderValue').innerText = Math.round(aov).toLocaleString() + ' {{ currency_code }}';
3777
 
@@ -4147,6 +4203,8 @@ def create_environment():
4147
  "system_mode": "both",
4148
  "hide_stock_online": False,
4149
  "closed_catalog_enabled": False,
 
 
4150
  "theme": "light",
4151
  "customer_fields": {'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False},
4152
  "socials": {
@@ -4338,7 +4396,7 @@ def finish_assembly(env_id, order_id):
4338
  new_cart.append(item)
4339
 
4340
  order['cart'] = new_cart
4341
- update_order_totals(order, data['settings'].get('business_type', 'mixed'))
4342
  save_env_data(env_id, data)
4343
 
4344
  return jsonify({"success": True})
@@ -4369,7 +4427,7 @@ def process_return(env_id, order_id):
4369
  restore_stock(c_key, item.get('product_id'), item.get('variant_idx', -1), ret_qty, data['products'])
4370
  break
4371
 
4372
- update_order_totals(order, data['settings'].get('business_type', 'mixed'))
4373
  if order['total_price'] <= 0:
4374
  order['status'] = 'returned'
4375
 
@@ -4446,10 +4504,11 @@ def create_order(env_id):
4446
  "customer_zip": customer_zip,
4447
  "customer_whatsapp": customer_whatsapp,
4448
  "global_discount": global_discount,
4449
- "assembled": {}
 
4450
  }
4451
 
4452
- update_order_totals(new_order, data['settings'].get('business_type', 'mixed'))
4453
 
4454
  if order_status == 'pos' and data['settings'].get('track_inventory', False) and data['settings'].get('system_mode', 'both') not in ['external', 'light_external']:
4455
  deduct_stock(processed_cart, data['products'])
@@ -4525,7 +4584,7 @@ def edit_order(env_id, order_id):
4525
  order['cart'].remove(item)
4526
  break
4527
 
4528
- update_order_totals(order, data['settings'].get('business_type', 'mixed'))
4529
  save_env_data(env_id, data)
4530
 
4531
  return jsonify({"success": True, "total_price": order['total_price']})
@@ -4554,7 +4613,7 @@ def apply_discount(env_id, order_id):
4554
  except ValueError:
4555
  item['discount'] = 0
4556
 
4557
- update_order_totals(order, data['settings'].get('business_type', 'mixed'))
4558
  save_env_data(env_id, data)
4559
  flash('Скидка успешно применена.', 'success')
4560
  return redirect(url_for('admin', env_id=env_id))
@@ -4642,6 +4701,7 @@ def reports(env_id):
4642
  return render_template_string(
4643
  REPORTS_TEMPLATE,
4644
  env_id=env_id,
 
4645
  currency_code=settings.get('currency', 'T'),
4646
  orders_json=json.dumps(orders_list)
4647
  )
@@ -4778,6 +4838,12 @@ def admin(env_id):
4778
  settings['admin_password_enabled'] = 'admin_password_enabled' in request.form
4779
  settings['admin_password'] = request.form.get('admin_password', '').strip()
4780
 
 
 
 
 
 
 
4781
  settings['customer_fields'] = {
4782
  'name': 'cf_name' in request.form,
4783
  'phone': 'cf_phone' in request.form,
 
186
  'system_mode': 'both',
187
  'hide_stock_online': False,
188
  'closed_catalog_enabled': False,
189
+ 'commission_enabled': False,
190
+ 'commission_percent': 0.0,
191
  'theme': 'light',
192
  'customer_fields': {
193
  'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False
 
228
  if 'system_mode' not in settings: settings['system_mode'] = 'both'; changed = True
229
  if 'hide_stock_online' not in settings: settings['hide_stock_online'] = False; changed = True
230
  if 'closed_catalog_enabled' not in settings: settings['closed_catalog_enabled'] = False; changed = True
231
+ if 'commission_enabled' not in settings: settings['commission_enabled'] = False; changed = True
232
+ if 'commission_percent' not in settings: settings['commission_percent'] = 0.0; changed = True
233
  if 'theme' not in settings: settings['theme'] = 'light'; changed = True
234
  if 'customer_fields' not in settings:
235
  settings['customer_fields'] = {'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False}
 
267
  if 'staff_name' not in order: order['staff_name'] = ''; changed = True
268
  if 'assembled' not in order: order['assembled'] = {}; changed = True
269
  if 'global_discount' not in order: order['global_discount'] = 0; changed = True
270
+ if 'commission_amount' not in order: order['commission_amount'] = 0; changed = True
271
  for item in order.get('cart', []):
272
  if 'discount' not in item: item['discount'] = 0; changed = True
273
  if 'category' not in item: item['category'] = 'Без категории'; changed = True
 
322
  'system_mode': 'both',
323
  'hide_stock_online': False,
324
  'closed_catalog_enabled': False,
325
+ 'commission_enabled': False,
326
+ 'commission_percent': 0.0,
327
  'theme': 'light',
328
  'customer_fields': {'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False},
329
  'socials': {
 
341
  all_data[env_id] = env_data
342
  save_data(all_data)
343
 
344
+ def update_order_totals(order, settings):
345
+ business_type = settings.get('business_type', 'mixed')
346
+ commission_enabled = settings.get('commission_enabled', False)
347
+ commission_percent = float(settings.get('commission_percent', 0.0))
348
  total = 0
349
  global_discount = float(order.get('global_discount', 0))
350
  for i in order['cart']:
 
373
  total += item_total
374
 
375
  total = max(0, total - global_discount)
376
+
377
+ if commission_enabled and commission_percent > 0:
378
+ commission_amount = total * (commission_percent / 100.0)
379
+ order['commission_amount'] = round(commission_amount, 2)
380
+ total += commission_amount
381
+ else:
382
+ order['commission_amount'] = 0
383
+
384
  order['total_price'] = round(total, 2)
385
 
386
  def is_order_fully_assembled(order):
 
985
  const hideStockOnline = {{ 'true' if settings.hide_stock_online else 'false' }};
986
  const businessType = '{{ settings.business_type }}';
987
  const cFields = {{ settings.customer_fields|tojson }};
988
+ const commissionEnabled = {{ 'true' if settings.commission_enabled else 'false' }};
989
+ const commissionPercent = {{ settings.commission_percent | default(0) }};
990
 
991
  let cart = {};
992
  let currentGalleryPhotos =[];
 
1394
 
1395
  total = total - globalDisc;
1396
  if(total < 0) total = 0;
1397
+
1398
+ if (commissionEnabled && commissionPercent > 0) {
1399
+ total += total * (commissionPercent / 100.0);
1400
+ }
1401
 
1402
  const cartBar = document.getElementById('cartBar');
1403
  if (total > 0 || Object.keys(cart).length > 0) {
 
1451
  </div>
1452
  `;
1453
  }
1454
+
1455
+ if (commissionEnabled && commissionPercent > 0) {
1456
+ list.innerHTML += `<div style="text-align: right; font-size: 0.85rem; color: #0984e3; margin-top: 10px; font-weight: 600;">Включая комиссию ${commissionPercent}%</div>`;
1457
+ }
1458
  }
1459
 
1460
  function openCartModal() {
 
1955
  {% if order.global_discount > 0 %}
1956
  <div style="color:#e17055; font-size:0.9rem; margin-bottom:5px;">Применена общая скидка: -{{ order.global_discount }} {{ currency_code }}</div>
1957
  {% endif %}
1958
+ {% if settings.commission_enabled and order.commission_amount and order.commission_amount > 0 %}
1959
+ <div style="color:#0984e3; font-size:0.9rem; margin-bottom:5px;">Комиссия ({{ settings.commission_percent }}%): +{{ order.commission_amount }} {{ currency_code }}</div>
1960
+ {% endif %}
1961
  Итого:
1962
  </td>
1963
  <td>{{ order.total_price }} {{ currency_code }}</td>
 
2733
  <label><input type="checkbox" name="closed_catalog_enabled" {% if settings.closed_catalog_enabled %}checked{% endif %}> Включить закрытый каталог (доступ только по паролю из раздела пользователей)</label>
2734
  {% endif %}
2735
 
2736
+ <div style="font-weight: 600; margin-bottom: 5px; border-top: 1px solid var(--border); padding-top: 15px; margin-top: 10px;">Комиссия на заказы:</div>
2737
+ <div class="social-item" style="margin-bottom: 10px;">
2738
+ <label style="width: auto; margin-right: 15px;"><input type="checkbox" name="commission_enabled" {% if settings.commission_enabled %}checked{% endif %}> Добавить комиссию</label>
2739
+ <input type="number" step="0.01" name="commission_percent" value="{{ settings.commission_percent }}" placeholder="Процент (например, 0.95)" style="flex: 1; min-width: 150px;">
2740
+ </div>
2741
+
2742
  <div style="font-weight: 600; margin-bottom: 5px; border-top: 1px solid var(--border); padding-top: 15px; margin-top: 10px;">Безопасность (Админ-панель):</div>
2743
  <div class="social-item" style="margin-bottom: 10px;">
2744
  <label style="width: auto; margin-right: 15px;"><input type="checkbox" name="admin_password_enabled" {% if settings.admin_password_enabled %}checked{% endif %}> Пароль на вход</label>
 
3575
  .tabs { display: flex; gap: 10px; margin-bottom: 20px; border-bottom: 2px solid var(--border); padding-bottom: 10px; flex-wrap: wrap; }
3576
  .tab { padding: 10px 20px; cursor: pointer; font-weight: 600; color: #636e72; border-radius: 8px; transition: background 0.2s; }
3577
  .tab:hover { background: #e9ecef; }
3578
+ .tab.active {background: var(--primary); color: #fff; }
3579
 
3580
  .tab-content { display: none; }
3581
  .tab-content.active { display: block; }
 
3652
  <i class="fas fa-undo icon"></i>
3653
  </div>
3654
  </div>
3655
+ {% if settings.commission_enabled %}
3656
+ <div class="stat-card">
3657
+ <div class="stat-card-inner">
3658
+ <div class="title">Сумма комиссий ({{ settings.commission_percent }}%)</div>
3659
+ <div class="value" id="totalCommissions" style="color:var(--info);">0</div>
3660
+ <i class="fas fa-percentage icon"></i>
3661
+ </div>
3662
+ </div>
3663
+ {% endif %}
3664
  </div>
3665
 
3666
  <div class="table-container">
 
3769
  let totalRet = 0;
3770
  let totalItems = 0;
3771
  let ordersCount = 0;
3772
+ let totalCommissions = 0;
3773
 
3774
  let staffSales = {};
3775
  let productSales = {};
 
3782
  } else {
3783
  totalRev += o.total_price;
3784
  ordersCount++;
3785
+
3786
+ if (o.commission_amount) {
3787
+ totalCommissions += o.commission_amount;
3788
+ }
3789
 
3790
  const staff = o.staff_name || 'Онлайн (Без сотрудника)';
3791
  if(!staffSales[staff]) staffSales[staff] = { orders: 0, sum: 0 };
 
3823
  document.getElementById('totalReturns').innerText = totalRet.toLocaleString() + ' {{ currency_code }}';
3824
  document.getElementById('totalItemsSold').innerText = totalItems.toLocaleString();
3825
 
3826
+ let tcEl = document.getElementById('totalCommissions');
3827
+ if (tcEl) {
3828
+ tcEl.innerText = totalCommissions.toLocaleString() + ' {{ currency_code }}';
3829
+ }
3830
+
3831
  let aov = ordersCount > 0 ? (totalRev / ordersCount) : 0;
3832
  document.getElementById('avgOrderValue').innerText = Math.round(aov).toLocaleString() + ' {{ currency_code }}';
3833
 
 
4203
  "system_mode": "both",
4204
  "hide_stock_online": False,
4205
  "closed_catalog_enabled": False,
4206
+ "commission_enabled": False,
4207
+ "commission_percent": 0.0,
4208
  "theme": "light",
4209
  "customer_fields": {'name': True, 'phone': True, 'city': True, 'address': False, 'zip': False},
4210
  "socials": {
 
4396
  new_cart.append(item)
4397
 
4398
  order['cart'] = new_cart
4399
+ update_order_totals(order, data['settings'])
4400
  save_env_data(env_id, data)
4401
 
4402
  return jsonify({"success": True})
 
4427
  restore_stock(c_key, item.get('product_id'), item.get('variant_idx', -1), ret_qty, data['products'])
4428
  break
4429
 
4430
+ update_order_totals(order, data['settings'])
4431
  if order['total_price'] <= 0:
4432
  order['status'] = 'returned'
4433
 
 
4504
  "customer_zip": customer_zip,
4505
  "customer_whatsapp": customer_whatsapp,
4506
  "global_discount": global_discount,
4507
+ "assembled": {},
4508
+ "commission_amount": 0
4509
  }
4510
 
4511
+ update_order_totals(new_order, data['settings'])
4512
 
4513
  if order_status == 'pos' and data['settings'].get('track_inventory', False) and data['settings'].get('system_mode', 'both') not in ['external', 'light_external']:
4514
  deduct_stock(processed_cart, data['products'])
 
4584
  order['cart'].remove(item)
4585
  break
4586
 
4587
+ update_order_totals(order, data['settings'])
4588
  save_env_data(env_id, data)
4589
 
4590
  return jsonify({"success": True, "total_price": order['total_price']})
 
4613
  except ValueError:
4614
  item['discount'] = 0
4615
 
4616
+ update_order_totals(order, data['settings'])
4617
  save_env_data(env_id, data)
4618
  flash('Скидка успешно применена.', 'success')
4619
  return redirect(url_for('admin', env_id=env_id))
 
4701
  return render_template_string(
4702
  REPORTS_TEMPLATE,
4703
  env_id=env_id,
4704
+ settings=settings,
4705
  currency_code=settings.get('currency', 'T'),
4706
  orders_json=json.dumps(orders_list)
4707
  )
 
4838
  settings['admin_password_enabled'] = 'admin_password_enabled' in request.form
4839
  settings['admin_password'] = request.form.get('admin_password', '').strip()
4840
 
4841
+ settings['commission_enabled'] = 'commission_enabled' in request.form
4842
+ try:
4843
+ settings['commission_percent'] = float(request.form.get('commission_percent', 0.0))
4844
+ except ValueError:
4845
+ settings['commission_percent'] = 0.0
4846
+
4847
  settings['customer_fields'] = {
4848
  'name': 'cf_name' in request.form,
4849
  'phone': 'cf_phone' in request.form,