fnschool 20250109.80531.837__py3-none-any.whl → 20251011.80531.840__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fnschool might be problematic. Click here for more details.
- fnschoo1/__init__.py +52 -0
- fnschoo1/canteen/admin.py +3 -0
- fnschoo1/canteen/apps.py +6 -0
- fnschoo1/canteen/forms.py +84 -0
- fnschoo1/canteen/migrations/0001_initial.py +119 -0
- fnschoo1/canteen/migrations/0002_ingredient_is_disabled.py +20 -0
- fnschoo1/canteen/migrations/0003_consumption_is_disabled_alter_ingredient_is_disabled.py +23 -0
- fnschoo1/canteen/migrations/0004_alter_ingredient_name_category_and_more.py +66 -0
- fnschoo1/canteen/migrations/0005_alter_category_created_at_alter_category_name_and_more.py +41 -0
- fnschoo1/canteen/migrations/0006_category_is_disabled_alter_category_user_and_more.py +49 -0
- fnschoo1/canteen/migrations/0007_alter_consumption_amount_used_and_more.py +30 -0
- fnschoo1/canteen/migrations/0008_category_abbreviation_mealtype.py +67 -0
- fnschoo1/canteen/migrations/0009_alter_category_abbreviation_and_more.py +55 -0
- fnschoo1/canteen/migrations/0010_alter_consumption_options_alter_ingredient_options_and_more.py +215 -0
- fnschoo1/canteen/migrations/0011_category_pin_to_consumptions_top.py +23 -0
- fnschoo1/canteen/migrations/0012_alter_ingredient_storage_date.py +18 -0
- fnschoo1/canteen/models.py +179 -0
- fnschoo1/canteen/templates/canteen/category/create.html +17 -0
- fnschoo1/canteen/templates/canteen/category/delete.html +61 -0
- fnschoo1/canteen/templates/canteen/category/list.html +63 -0
- fnschoo1/canteen/templates/canteen/category/update.html +23 -0
- fnschoo1/canteen/templates/canteen/close.html +11 -0
- fnschoo1/canteen/templates/canteen/consumption/_create.html +19 -0
- fnschoo1/canteen/templates/canteen/consumption/create.html +456 -0
- fnschoo1/canteen/templates/canteen/ingredient/close.html +11 -0
- fnschoo1/canteen/templates/canteen/ingredient/create.html +19 -0
- fnschoo1/canteen/templates/canteen/ingredient/create_one.html +17 -0
- fnschoo1/canteen/templates/canteen/ingredient/delete.html +41 -0
- fnschoo1/canteen/templates/canteen/ingredient/list.html +128 -0
- fnschoo1/canteen/templates/canteen/ingredient/update.html +23 -0
- fnschoo1/canteen/templates/canteen/meal_type/create.html +17 -0
- fnschoo1/canteen/templates/canteen/meal_type/delete.html +56 -0
- fnschoo1/canteen/templates/canteen/meal_type/list.html +59 -0
- fnschoo1/canteen/templates/canteen/meal_type/update.html +23 -0
- fnschoo1/canteen/tests.py +3 -0
- fnschoo1/canteen/urls.py +116 -0
- fnschoo1/canteen/views.py +814 -0
- fnschoo1/canteen/workbook/generate.py +2098 -0
- fnschoo1/db.sqlite3 +0 -0
- fnschoo1/fnschool/__init__.py +13 -0
- fnschoo1/fnschool/asgi.py +16 -0
- fnschoo1/fnschool/settings.py +167 -0
- fnschoo1/fnschool/templatetags/fnschool_tags.py +27 -0
- fnschoo1/fnschool/urls.py +30 -0
- fnschoo1/fnschool/views.py +9 -0
- fnschoo1/fnschool/wsgi.py +16 -0
- fnschoo1/locale/en/LC_MESSAGES/django.mo +0 -0
- fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- fnschoo1/manage.py +25 -0
- fnschoo1/profiles/admin.py +27 -0
- fnschoo1/profiles/apps.py +12 -0
- fnschoo1/profiles/forms.py +67 -0
- fnschoo1/profiles/migrations/0001_initial.py +192 -0
- fnschoo1/profiles/migrations/0002_alter_profile_bio.py +20 -0
- fnschoo1/profiles/migrations/0003_alter_profile_options_alter_profile_address_and_more.py +67 -0
- fnschoo1/profiles/migrations/0004_profile_gender.py +26 -0
- fnschoo1/profiles/migrations/0005_alter_profile_gender.py +23 -0
- fnschoo1/profiles/models.py +60 -0
- fnschoo1/profiles/signals.py +20 -0
- fnschoo1/profiles/templates/profiles/create.html +16 -0
- fnschoo1/profiles/templates/profiles/detail.html +14 -0
- fnschoo1/profiles/templates/profiles/edit.html +12 -0
- fnschoo1/profiles/templates/profiles/log_in.html +20 -0
- fnschoo1/profiles/templates/profiles/log_out.html +12 -0
- fnschoo1/profiles/tests.py +3 -0
- fnschoo1/profiles/urls.py +15 -0
- fnschoo1/profiles/views.py +63 -0
- fnschoo1/static/css/bootstrap.min.css +11776 -0
- fnschoo1/static/css/fnschool.css +26 -0
- fnschoo1/static/js/bootstrap.bundle.min.js +4223 -0
- fnschoo1/static/js/bootstrap.min.js +2919 -0
- fnschoo1/static/js/fnschool.js +84 -0
- fnschoo1/static/js/jquery.min.js +5413 -0
- fnschoo1/static/js/jquery.slim.min.js +4331 -0
- fnschoo1/static/js/popper.min.js +1306 -0
- fnschoo1/static_collected/admin/css/autocomplete.css +377 -0
- fnschoo1/static_collected/admin/css/base.css +1224 -0
- fnschoo1/static_collected/admin/css/changelists.css +334 -0
- fnschoo1/static_collected/admin/css/dark_mode.css +136 -0
- fnschoo1/static_collected/admin/css/dashboard.css +30 -0
- fnschoo1/static_collected/admin/css/forms.css +546 -0
- fnschoo1/static_collected/admin/css/login.css +62 -0
- fnschoo1/static_collected/admin/css/nav_sidebar.css +145 -0
- fnschoo1/static_collected/admin/css/responsive.css +1043 -0
- fnschoo1/static_collected/admin/css/responsive_rtl.css +84 -0
- fnschoo1/static_collected/admin/css/rtl.css +311 -0
- fnschoo1/static_collected/admin/css/vendor/select2/select2.css +708 -0
- fnschoo1/static_collected/admin/css/vendor/select2/select2.min.css +1 -0
- fnschoo1/static_collected/admin/css/widgets.css +639 -0
- fnschoo1/static_collected/admin/js/SelectBox.js +128 -0
- fnschoo1/static_collected/admin/js/SelectFilter2.js +503 -0
- fnschoo1/static_collected/admin/js/actions.js +232 -0
- fnschoo1/static_collected/admin/js/admin/DateTimeShortcuts.js +496 -0
- fnschoo1/static_collected/admin/js/admin/RelatedObjectLookups.js +276 -0
- fnschoo1/static_collected/admin/js/autocomplete.js +33 -0
- fnschoo1/static_collected/admin/js/calendar.js +251 -0
- fnschoo1/static_collected/admin/js/cancel.js +29 -0
- fnschoo1/static_collected/admin/js/change_form.js +21 -0
- fnschoo1/static_collected/admin/js/collapse.js +43 -0
- fnschoo1/static_collected/admin/js/core.js +174 -0
- fnschoo1/static_collected/admin/js/filters.js +37 -0
- fnschoo1/static_collected/admin/js/inlines.js +439 -0
- fnschoo1/static_collected/admin/js/jquery.init.js +8 -0
- fnschoo1/static_collected/admin/js/nav_sidebar.js +81 -0
- fnschoo1/static_collected/admin/js/popup_response.js +24 -0
- fnschoo1/static_collected/admin/js/prepopulate.js +43 -0
- fnschoo1/static_collected/admin/js/prepopulate_init.js +20 -0
- fnschoo1/static_collected/admin/js/theme.js +57 -0
- fnschoo1/static_collected/admin/js/urlify.js +529 -0
- fnschoo1/static_collected/admin/js/vendor/jquery/jquery.js +10913 -0
- fnschoo1/static_collected/admin/js/vendor/jquery/jquery.min.js +2 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/af.js +43 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ar.js +36 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/az.js +33 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/bg.js +38 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/bn.js +39 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/bs.js +48 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ca.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/cs.js +62 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/da.js +37 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/de.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/dsb.js +51 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/el.js +43 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/en.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/es.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/et.js +38 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/eu.js +45 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/fa.js +42 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/fi.js +42 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/fr.js +43 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/gl.js +40 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/he.js +44 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/hi.js +40 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/hr.js +45 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/hsb.js +51 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/hu.js +44 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/hy.js +40 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/id.js +36 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/is.js +37 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/it.js +43 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ja.js +40 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ka.js +42 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/km.js +38 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ko.js +42 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/lt.js +45 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/lv.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/mk.js +42 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ms.js +38 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/nb.js +38 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ne.js +44 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/nl.js +46 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/pl.js +43 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ps.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/pt-BR.js +39 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/pt.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ro.js +43 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/ru.js +48 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/sk.js +61 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/sl.js +41 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/sq.js +43 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/sr-Cyrl.js +48 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/sr.js +48 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/sv.js +40 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/th.js +36 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/tk.js +36 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/tr.js +40 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/uk.js +59 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/vi.js +37 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/zh-CN.js +36 -0
- fnschoo1/static_collected/admin/js/vendor/select2/i18n/zh-TW.js +33 -0
- fnschoo1/static_collected/admin/js/vendor/select2/select2.full.js +7115 -0
- fnschoo1/static_collected/admin/js/vendor/select2/select2.full.min.js +2 -0
- fnschoo1/static_collected/admin/js/vendor/xregexp/xregexp.js +4993 -0
- fnschoo1/static_collected/admin/js/vendor/xregexp/xregexp.min.js +160 -0
- fnschoo1/static_collected/css/bootstrap.min.css +11776 -0
- fnschoo1/static_collected/css/fnschool.css +26 -0
- fnschoo1/static_collected/js/bootstrap.bundle.min.js +4223 -0
- fnschoo1/static_collected/js/bootstrap.min.js +2919 -0
- fnschoo1/static_collected/js/fnschool.js +84 -0
- fnschoo1/static_collected/js/jquery.min.js +5413 -0
- fnschoo1/static_collected/js/jquery.slim.min.js +4331 -0
- fnschoo1/static_collected/js/popper.min.js +1306 -0
- fnschoo1/templates/base/_css.html +1 -0
- fnschoo1/templates/base/_js.html +15 -0
- fnschoo1/templates/base/content.html +30 -0
- fnschoo1/templates/base/header_content_footer.html +35 -0
- fnschoo1/templates/close.html +11 -0
- fnschoo1/templates/home.html +51 -0
- fnschoo1/templates/includes/_footer.html +39 -0
- fnschoo1/templates/includes/_header.html +77 -0
- fnschoo1/templates/includes/_navigation.html +0 -0
- fnschoo1/templates/includes/_paginator.html +27 -0
- fnschoo1/templates/registration/logged_out.html +0 -0
- fnschoo1/templates/registration/login.html +0 -0
- fnschool-20251011.80531.840.dist-info/METADATA +179 -0
- fnschool-20251011.80531.840.dist-info/RECORD +207 -0
- {fnschool-20250109.80531.837.dist-info → fnschool-20251011.80531.840.dist-info}/WHEEL +1 -1
- fnschool-20251011.80531.840.dist-info/entry_points.txt +2 -0
- fnschool-20251011.80531.840.dist-info/top_level.txt +1 -0
- fnschool/__init__.py +0 -35
- fnschool/__main__.py +0 -16
- fnschool/app.py +0 -103
- fnschool/canteen/__init__.py +0 -3
- fnschool/canteen/__main__.py +0 -3
- fnschool/canteen/bill.py +0 -253
- fnschool/canteen/canteen.py +0 -1
- fnschool/canteen/canteen.toml +0 -61
- fnschool/canteen/config.py +0 -10
- fnschool/canteen/consuming.py +0 -53
- fnschool/canteen/currency.py +0 -17
- fnschool/canteen/data/bill.i18n.xlsx +0 -0
- fnschool/canteen/data/bill.xlsx +0 -0
- fnschool/canteen/data/consuming.xlsx +0 -0
- fnschool/canteen/data/purchase_list.xlsx +0 -0
- fnschool/canteen/entry.py +0 -40
- fnschool/canteen/food.py +0 -206
- fnschool/canteen/food_classes.py +0 -33
- fnschool/canteen/food_classes.toml +0 -64
- fnschool/canteen/operator.py +0 -91
- fnschool/canteen/path.py +0 -28
- fnschool/canteen/spreadsheet/base.py +0 -213
- fnschool/canteen/spreadsheet/consuming.py +0 -310
- fnschool/canteen/spreadsheet/consumingsum.py +0 -76
- fnschool/canteen/spreadsheet/cover.py +0 -64
- fnschool/canteen/spreadsheet/ctspreadsheet.py +0 -351
- fnschool/canteen/spreadsheet/food.py +0 -680
- fnschool/canteen/spreadsheet/inventory.py +0 -375
- fnschool/canteen/spreadsheet/merging.py +0 -340
- fnschool/canteen/spreadsheet/preconsuming.py +0 -329
- fnschool/canteen/spreadsheet/purchasing.py +0 -885
- fnschool/canteen/spreadsheet/purchasingsum.py +0 -110
- fnschool/canteen/spreadsheet/spreadsheet.py +0 -363
- fnschool/canteen/spreadsheet/translating.py +0 -12
- fnschool/canteen/spreadsheet/unwarehousing.py +0 -178
- fnschool/canteen/spreadsheet/unwarehousingsum.py +0 -11
- fnschool/canteen/spreadsheet/warehousing.py +0 -360
- fnschool/canteen/spreadsheet/workbook.py +0 -17
- fnschool/canteen/test.py +0 -97
- fnschool/config.py +0 -48
- fnschool/entry.py +0 -67
- fnschool/exam/__init__.py +0 -8
- fnschool/exam/data/parental_emails.xlsx +0 -0
- fnschool/exam/data/score.xlsx +0 -0
- fnschool/exam/email.py +0 -349
- fnschool/exam/entry.py +0 -36
- fnschool/exam/language.py +0 -19
- fnschool/exam/path.py +0 -24
- fnschool/exam/score.py +0 -1191
- fnschool/exam/subject.py +0 -20
- fnschool/exam/teacher.py +0 -54
- fnschool/external.py +0 -89
- fnschool/games/__init__.py +0 -1
- fnschool/games/__main__.py +0 -1
- fnschool/games/games.py +0 -1
- fnschool/inoutput.py +0 -97
- fnschool/language.py +0 -40
- fnschool/locales/en_US/LC_MESSAGES/fnschool.mo +0 -0
- fnschool/locales/zh_CN/LC_MESSAGES/fnschool.mo +0 -0
- fnschool/locales/zh_HK/LC_MESSAGES/fnschool.mo +0 -0
- fnschool/locales/zh_SG/LC_MESSAGES/fnschool.mo +0 -0
- fnschool/locales/zh_TW/LC_MESSAGES/fnschool.mo +0 -0
- fnschool/path.py +0 -45
- fnschool/test.py +0 -24
- fnschool/user.py +0 -262
- fnschool-20250109.80531.837.dist-info/METADATA +0 -342
- fnschool-20250109.80531.837.dist-info/RECORD +0 -78
- fnschool-20250109.80531.837.dist-info/entry_points.txt +0 -5
- fnschool-20250109.80531.837.dist-info/top_level.txt +0 -1
- /fnschool/canteen/consume.py → /fnschoo1/canteen/__init__.py +0 -0
- /fnschool/canteen/food_recount.toml → /fnschoo1/canteen/migrations/__init__.py +0 -0
- {fnschool/canteen/spreadsheet → fnschoo1/canteen/workbook}/__init__.py +0 -0
- /fnschool/exam/__main__.py → /fnschoo1/fnschool/templatetags/__init__.py +0 -0
- /fnschool/canteen/food_recounts.toml → /fnschoo1/profiles/__init__.py +0 -0
- /fnschool/canteen/warehouse.py → /fnschoo1/profiles/migrations/__init__.py +0 -0
- /fnschool/canteen/workbook.toml → /fnschoo1/templates/base/_content.html +0 -0
- {fnschool-20250109.80531.837.dist-info → fnschool-20251011.80531.840.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,885 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
from openpyxl.utils.cell import get_column_letter
|
|
4
|
-
import tomllib
|
|
5
|
-
from tkinter import filedialog, ttk
|
|
6
|
-
import tkinter as tk
|
|
7
|
-
|
|
8
|
-
from fnschool import *
|
|
9
|
-
from fnschool.canteen.path import *
|
|
10
|
-
from fnschool.canteen.food import *
|
|
11
|
-
from fnschool.canteen.spreadsheet.base import *
|
|
12
|
-
from openpyxl.worksheet.datavalidation import DataValidation
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Purchasing(Base):
|
|
16
|
-
def __init__(self, bill):
|
|
17
|
-
super().__init__(bill)
|
|
18
|
-
self.p_path_key = _("parent_path_of_purchasing_file")
|
|
19
|
-
self._path = None
|
|
20
|
-
|
|
21
|
-
condiment_class_names = ["调味类", "调味品类", "调味"]
|
|
22
|
-
self.condiment_class_names = (
|
|
23
|
-
condiment_class_names if is_zh_CN else condiment_class_names
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
self._food_name_col = [
|
|
27
|
-
None,
|
|
28
|
-
None,
|
|
29
|
-
["商品名称", "食材名称", "食品名称"],
|
|
30
|
-
]
|
|
31
|
-
self._unit_name_col = [
|
|
32
|
-
None,
|
|
33
|
-
None,
|
|
34
|
-
["订货单位", "食材单位", "订购单位", "计量单位"],
|
|
35
|
-
]
|
|
36
|
-
self._total_price_col = [
|
|
37
|
-
None,
|
|
38
|
-
None,
|
|
39
|
-
[
|
|
40
|
-
"总价",
|
|
41
|
-
"折后金额",
|
|
42
|
-
"总金额",
|
|
43
|
-
"折前金额",
|
|
44
|
-
],
|
|
45
|
-
]
|
|
46
|
-
self._xdate_col = [
|
|
47
|
-
None,
|
|
48
|
-
None,
|
|
49
|
-
["入库日期", "送货日期", "检查日期", "清点日期", "x日期", "日期"],
|
|
50
|
-
]
|
|
51
|
-
self._purchaser_col = [
|
|
52
|
-
None,
|
|
53
|
-
None,
|
|
54
|
-
[
|
|
55
|
-
"下单单位名",
|
|
56
|
-
"客户名称",
|
|
57
|
-
"购买者",
|
|
58
|
-
"购买者名称",
|
|
59
|
-
"顾客名称",
|
|
60
|
-
"购入单位名",
|
|
61
|
-
],
|
|
62
|
-
]
|
|
63
|
-
self._count_col = [
|
|
64
|
-
None,
|
|
65
|
-
None,
|
|
66
|
-
["总数", "数量", "发货数量", "记账数量", "总数量", "菜品数量"],
|
|
67
|
-
]
|
|
68
|
-
self._meal_type_col = [
|
|
69
|
-
None,
|
|
70
|
-
None,
|
|
71
|
-
["餐类", "订单类型", "餐型"],
|
|
72
|
-
]
|
|
73
|
-
self._abandoned_col = [
|
|
74
|
-
None,
|
|
75
|
-
None,
|
|
76
|
-
[
|
|
77
|
-
"不计",
|
|
78
|
-
"是不计",
|
|
79
|
-
"未入库",
|
|
80
|
-
"非入库",
|
|
81
|
-
"不需入库",
|
|
82
|
-
"是非入库",
|
|
83
|
-
],
|
|
84
|
-
]
|
|
85
|
-
self._inventory_col = [
|
|
86
|
-
None,
|
|
87
|
-
None,
|
|
88
|
-
[
|
|
89
|
-
"盘存",
|
|
90
|
-
"存余",
|
|
91
|
-
"结余",
|
|
92
|
-
"是结余",
|
|
93
|
-
"剩余",
|
|
94
|
-
"是剩余",
|
|
95
|
-
"是盘存",
|
|
96
|
-
],
|
|
97
|
-
]
|
|
98
|
-
|
|
99
|
-
self._food_class_col = [
|
|
100
|
-
"食材大类",
|
|
101
|
-
None,
|
|
102
|
-
["食材大类", "大类", "食材分类", "食材主类"],
|
|
103
|
-
]
|
|
104
|
-
self._wb = None
|
|
105
|
-
self._sheet = None
|
|
106
|
-
self._headers = None
|
|
107
|
-
self.edited_cell_font = Font(color="00FF0000")
|
|
108
|
-
self._cols = None
|
|
109
|
-
self._food_class_dv = None
|
|
110
|
-
self._pd_date = None
|
|
111
|
-
self._meal_types = None
|
|
112
|
-
|
|
113
|
-
self.essential_cols = [
|
|
114
|
-
False,
|
|
115
|
-
[
|
|
116
|
-
self._food_name_col,
|
|
117
|
-
self._unit_name_col,
|
|
118
|
-
self._total_price_col,
|
|
119
|
-
self._xdate_col,
|
|
120
|
-
self._purchaser_col,
|
|
121
|
-
self._count_col,
|
|
122
|
-
# self._food_class_col,
|
|
123
|
-
],
|
|
124
|
-
]
|
|
125
|
-
|
|
126
|
-
@property
|
|
127
|
-
def pd_data(self):
|
|
128
|
-
if self._pd_date is None:
|
|
129
|
-
self._pd_date = pd.read_excel(self.path)
|
|
130
|
-
return self._pd_date
|
|
131
|
-
|
|
132
|
-
@pd_data.setter
|
|
133
|
-
def pd_data(self, pd_data):
|
|
134
|
-
self._pd_date = pd_data
|
|
135
|
-
pass
|
|
136
|
-
|
|
137
|
-
@property
|
|
138
|
-
def food_class_dv(self):
|
|
139
|
-
if not self._food_class_dv:
|
|
140
|
-
self._food_class_dv = DataValidation(
|
|
141
|
-
type="list",
|
|
142
|
-
formula1=(
|
|
143
|
-
'"'
|
|
144
|
-
+ ",".join(["蔬菜类"] + list(self.food_classes.keys()))
|
|
145
|
-
+ '"'
|
|
146
|
-
),
|
|
147
|
-
)
|
|
148
|
-
return self._food_class_dv
|
|
149
|
-
|
|
150
|
-
def essential_cols_pass(self):
|
|
151
|
-
check_pass, cols = self.essential_cols
|
|
152
|
-
if check_pass:
|
|
153
|
-
return True
|
|
154
|
-
missed_cols = []
|
|
155
|
-
for col in cols:
|
|
156
|
-
col0 = self.get_optional_col(col)
|
|
157
|
-
if not col[1]:
|
|
158
|
-
missed_cols.append(col[-1][0])
|
|
159
|
-
if len(missed_cols) > 0:
|
|
160
|
-
print_error(
|
|
161
|
-
_('Column "{0}" was no found.').format("".join(missed_cols))
|
|
162
|
-
if len(col) == 1
|
|
163
|
-
else _('Columns "{0}" were no found.').format(
|
|
164
|
-
_(",").join(missed_cols)
|
|
165
|
-
)
|
|
166
|
-
)
|
|
167
|
-
self.essential_cols[0] = False
|
|
168
|
-
return False
|
|
169
|
-
self.essential_cols[0] = True
|
|
170
|
-
return True
|
|
171
|
-
|
|
172
|
-
def get_col(self, col):
|
|
173
|
-
|
|
174
|
-
if not self.essential_cols_pass():
|
|
175
|
-
exit()
|
|
176
|
-
|
|
177
|
-
if not col[1]:
|
|
178
|
-
col0 = [
|
|
179
|
-
(n, self.headers.index(n) + 1)
|
|
180
|
-
for n in self.headers
|
|
181
|
-
if n in col[2]
|
|
182
|
-
]
|
|
183
|
-
|
|
184
|
-
if len(col0) < 1:
|
|
185
|
-
print_error(
|
|
186
|
-
_('Column "{0}" was not found. Exit').format(col[2][0])
|
|
187
|
-
)
|
|
188
|
-
exit()
|
|
189
|
-
|
|
190
|
-
col0 = col0[-1]
|
|
191
|
-
col[0] = col0[0]
|
|
192
|
-
col[1] = col0[1]
|
|
193
|
-
|
|
194
|
-
return col
|
|
195
|
-
|
|
196
|
-
def get_optional_col(self, col):
|
|
197
|
-
if not col[1]:
|
|
198
|
-
col0 = [
|
|
199
|
-
(n, self.headers.index(n) + 1)
|
|
200
|
-
for n in self.headers
|
|
201
|
-
if n in col[2]
|
|
202
|
-
]
|
|
203
|
-
if len(col0) < 1:
|
|
204
|
-
return None
|
|
205
|
-
col0 = col0[-1]
|
|
206
|
-
col[0] = col0[0]
|
|
207
|
-
col[1] = col0[1]
|
|
208
|
-
|
|
209
|
-
return col
|
|
210
|
-
|
|
211
|
-
@property
|
|
212
|
-
def col_indexes(self):
|
|
213
|
-
indexes = [c[1] for c in self.cols]
|
|
214
|
-
indexes = sorted(indexes)
|
|
215
|
-
return indexes
|
|
216
|
-
|
|
217
|
-
@property
|
|
218
|
-
def cols(self):
|
|
219
|
-
if not self._cols:
|
|
220
|
-
self._cols = [
|
|
221
|
-
self.xdate_col,
|
|
222
|
-
self.purchaser_col,
|
|
223
|
-
self.food_name_col,
|
|
224
|
-
self.food_class_col,
|
|
225
|
-
self.unit_name_col,
|
|
226
|
-
self.count_col,
|
|
227
|
-
self.total_price_col,
|
|
228
|
-
self.abandoned_col,
|
|
229
|
-
self.inventory_col,
|
|
230
|
-
self.meal_type_col,
|
|
231
|
-
]
|
|
232
|
-
return self._cols
|
|
233
|
-
|
|
234
|
-
@property
|
|
235
|
-
def xdate_col(self):
|
|
236
|
-
return self.get_col(self._xdate_col)
|
|
237
|
-
|
|
238
|
-
@property
|
|
239
|
-
def purchaser_col(self):
|
|
240
|
-
return self.get_col(self._purchaser_col)
|
|
241
|
-
|
|
242
|
-
@property
|
|
243
|
-
def meal_type_col(self):
|
|
244
|
-
return self.get_optional_col(self._meal_type_col)
|
|
245
|
-
|
|
246
|
-
@property
|
|
247
|
-
def food_name_col(self):
|
|
248
|
-
return self.get_col(self._food_name_col)
|
|
249
|
-
|
|
250
|
-
@property
|
|
251
|
-
def food_class_col(self):
|
|
252
|
-
return self.get_col(self._food_class_col)
|
|
253
|
-
|
|
254
|
-
@property
|
|
255
|
-
def unit_name_col(self):
|
|
256
|
-
return self.get_col(self._unit_name_col)
|
|
257
|
-
|
|
258
|
-
@property
|
|
259
|
-
def count_col(self):
|
|
260
|
-
return self.get_col(self._count_col)
|
|
261
|
-
|
|
262
|
-
@property
|
|
263
|
-
def total_price_col(self):
|
|
264
|
-
return self.get_col(self._total_price_col)
|
|
265
|
-
|
|
266
|
-
@property
|
|
267
|
-
def abandoned_col(self):
|
|
268
|
-
return self.get_col(self._abandoned_col)
|
|
269
|
-
|
|
270
|
-
@property
|
|
271
|
-
def inventory_col(self):
|
|
272
|
-
return self.get_col(self._inventory_col)
|
|
273
|
-
|
|
274
|
-
@property
|
|
275
|
-
def wb(self):
|
|
276
|
-
if not self._wb:
|
|
277
|
-
print_info(_('Loading data from "{0}".').format(self.path))
|
|
278
|
-
self._wb = load_workbook(self.path)
|
|
279
|
-
return self._wb
|
|
280
|
-
|
|
281
|
-
@wb.deleter
|
|
282
|
-
def wb(self):
|
|
283
|
-
self._wb = None
|
|
284
|
-
self._sheet = None
|
|
285
|
-
self._headers = None
|
|
286
|
-
|
|
287
|
-
@property
|
|
288
|
-
def sheet(self):
|
|
289
|
-
if not self._sheet:
|
|
290
|
-
self._sheet = self.wb.active
|
|
291
|
-
return self._sheet
|
|
292
|
-
|
|
293
|
-
@property
|
|
294
|
-
def headers(self):
|
|
295
|
-
if not self._headers:
|
|
296
|
-
self._headers = [
|
|
297
|
-
v
|
|
298
|
-
for v in [
|
|
299
|
-
self.sheet.cell(1, col_index).value
|
|
300
|
-
for col_index in range(1, self.sheet.max_column + 1)
|
|
301
|
-
]
|
|
302
|
-
if v
|
|
303
|
-
]
|
|
304
|
-
return self._headers
|
|
305
|
-
|
|
306
|
-
def add_class_col(self):
|
|
307
|
-
if not any(
|
|
308
|
-
[fclass in self.headers for fclass in self._food_class_col[2]]
|
|
309
|
-
):
|
|
310
|
-
fname_col_index = (
|
|
311
|
-
max(
|
|
312
|
-
[
|
|
313
|
-
self._headers.index(n)
|
|
314
|
-
for n in self._headers
|
|
315
|
-
if n in self._food_name_col[2]
|
|
316
|
-
]
|
|
317
|
-
)
|
|
318
|
-
+ 1
|
|
319
|
-
)
|
|
320
|
-
fclass_col_index = fname_col_index + 1
|
|
321
|
-
self.sheet.insert_cols(fclass_col_index, 1)
|
|
322
|
-
self.sheet.cell(1, fclass_col_index, self._food_class_col[0])
|
|
323
|
-
for row_index in range(2, self.sheet.max_row + 1):
|
|
324
|
-
fname = self.sheet.cell(row_index, fname_col_index).value
|
|
325
|
-
if not fname:
|
|
326
|
-
break
|
|
327
|
-
cell = self.sheet.cell(row_index, fclass_col_index)
|
|
328
|
-
cell.value = self.get_food_class(fname)
|
|
329
|
-
cell.font = self.edited_cell_font
|
|
330
|
-
self.food_class_dv.add(cell)
|
|
331
|
-
self.wb.save(self.path)
|
|
332
|
-
self.wb.close()
|
|
333
|
-
del self.wb
|
|
334
|
-
print_info(
|
|
335
|
-
_(
|
|
336
|
-
'Column "{0}" has been updated, '
|
|
337
|
-
+ "feel free to open new issue if some "
|
|
338
|
-
+ "food with the wrong class ({1}). "
|
|
339
|
-
).format(self._food_class_col[0], get_new_issue_url())
|
|
340
|
-
)
|
|
341
|
-
print_warning(
|
|
342
|
-
_(
|
|
343
|
-
"Ok, I'd like to check and update it. "
|
|
344
|
-
+ "(Press any key to check the file)"
|
|
345
|
-
)
|
|
346
|
-
)
|
|
347
|
-
get_input()
|
|
348
|
-
open_path(self.path)
|
|
349
|
-
print_info(
|
|
350
|
-
_(
|
|
351
|
-
"I have checked it, all classes of "
|
|
352
|
-
+ "food are right, and I closed the "
|
|
353
|
-
+ "file. (Press any key to continue)"
|
|
354
|
-
)
|
|
355
|
-
)
|
|
356
|
-
get_input()
|
|
357
|
-
pass
|
|
358
|
-
|
|
359
|
-
@property
|
|
360
|
-
def food_classes(self):
|
|
361
|
-
food_classes = self.bill.food_classes
|
|
362
|
-
return food_classes
|
|
363
|
-
|
|
364
|
-
def food_name_like(self, name, like):
|
|
365
|
-
not_likes = None
|
|
366
|
-
if "!" in like:
|
|
367
|
-
like = like.split("!")
|
|
368
|
-
not_likes = like[1:]
|
|
369
|
-
like = like[0]
|
|
370
|
-
|
|
371
|
-
result = None
|
|
372
|
-
like_value = like.replace("*", "")
|
|
373
|
-
if like.startswith("*") and not like.endswith("*"):
|
|
374
|
-
result = name.endswith(like_value)
|
|
375
|
-
elif like.endswith("*") and not like.startswith("*"):
|
|
376
|
-
result = name.startswith(like_value)
|
|
377
|
-
elif not "*" in like:
|
|
378
|
-
result = like_value == name
|
|
379
|
-
elif like.startswith("*") and like.endswith("*"):
|
|
380
|
-
result = like_value in name
|
|
381
|
-
|
|
382
|
-
if not_likes:
|
|
383
|
-
result = result and not any(
|
|
384
|
-
[self.food_name_like(name, nl) for nl in not_likes]
|
|
385
|
-
)
|
|
386
|
-
return result
|
|
387
|
-
pass
|
|
388
|
-
|
|
389
|
-
def get_food_class(self, name):
|
|
390
|
-
food_classes = self.food_classes
|
|
391
|
-
for fclass, name_likes in food_classes.items():
|
|
392
|
-
for name_like in name_likes:
|
|
393
|
-
if self.food_name_like(name, name_like):
|
|
394
|
-
return fclass
|
|
395
|
-
return "蔬菜类"
|
|
396
|
-
|
|
397
|
-
@property
|
|
398
|
-
def path(self):
|
|
399
|
-
p_dpath = self.config.get(self.p_path_key)
|
|
400
|
-
initialdir = (
|
|
401
|
-
p_dpath
|
|
402
|
-
if (p_dpath and Path(p_dpath).exists())
|
|
403
|
-
else ((Path.home() / "Downloads").as_posix())
|
|
404
|
-
)
|
|
405
|
-
if not self._path:
|
|
406
|
-
print_info(
|
|
407
|
-
_(
|
|
408
|
-
"{0} need a purchasing list file, "
|
|
409
|
-
+ "and it's file type should be '.xlsx'. "
|
|
410
|
-
+ "The column names of it:"
|
|
411
|
-
).format(app_name)
|
|
412
|
-
+ _(
|
|
413
|
-
""
|
|
414
|
-
+ "\n\tcolumn type example"
|
|
415
|
-
+ "\n\t送货日期 Text 2024-03-01"
|
|
416
|
-
+ "\n\t食材名称 Text 香菜"
|
|
417
|
-
+ "\n\t餐类 Text 正餐 (Optional)"
|
|
418
|
-
+ "\n\t数量 Number 20"
|
|
419
|
-
+ "\n\t计量单位 Text 斤"
|
|
420
|
-
+ "\n\t总价 Number 20.0"
|
|
421
|
-
+ "\n\t购买者 Text "
|
|
422
|
-
+ "\n\t是不计 Text y"
|
|
423
|
-
+ "\n\t是结余 Text y"
|
|
424
|
-
)
|
|
425
|
-
)
|
|
426
|
-
print_info(_("Please select a purchasing file."))
|
|
427
|
-
filetypes = ((_("Spreadsheet Files"), "*.xlsx"),)
|
|
428
|
-
|
|
429
|
-
tkroot = tk.Tk()
|
|
430
|
-
tkroot.withdraw()
|
|
431
|
-
|
|
432
|
-
filename = filedialog.askopenfilename(
|
|
433
|
-
parent=tkroot,
|
|
434
|
-
title=_("Please select the purchasing file"),
|
|
435
|
-
initialdir=initialdir,
|
|
436
|
-
filetypes=filetypes,
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
if filename is None or filename == ():
|
|
440
|
-
print_warning(_("No file was selected, exit."))
|
|
441
|
-
exit()
|
|
442
|
-
return None
|
|
443
|
-
print_info(
|
|
444
|
-
_('Purchasing list file "{0}" has been selected.').format(
|
|
445
|
-
filename
|
|
446
|
-
)
|
|
447
|
-
)
|
|
448
|
-
self._path = filename
|
|
449
|
-
self.config.save(self.p_path_key, Path(self._path).parent.as_posix())
|
|
450
|
-
return self._path
|
|
451
|
-
|
|
452
|
-
def update_data_validations(self):
|
|
453
|
-
if not self.food_class_dv in self.sheet.data_validations:
|
|
454
|
-
self.sheet.add_data_validation(self.food_class_dv)
|
|
455
|
-
|
|
456
|
-
def update(self):
|
|
457
|
-
self.update_data_validations()
|
|
458
|
-
self.add_class_col()
|
|
459
|
-
self.update_inventories()
|
|
460
|
-
|
|
461
|
-
def get_meal_types(self):
|
|
462
|
-
data = self.pd_data
|
|
463
|
-
col = self.meal_type_col
|
|
464
|
-
if not col:
|
|
465
|
-
return []
|
|
466
|
-
self._meal_types = list(set(data.loc[:, col[0]]))
|
|
467
|
-
if len(self._meal_types) > 0:
|
|
468
|
-
return self._meal_types
|
|
469
|
-
return None
|
|
470
|
-
|
|
471
|
-
def update_inventories(self):
|
|
472
|
-
|
|
473
|
-
merged_ranges = list(self.sheet.merged_cells.ranges)
|
|
474
|
-
for cell_group in merged_ranges:
|
|
475
|
-
sheet.unmerge_cells(str(cell_group))
|
|
476
|
-
|
|
477
|
-
inventories_len = len(
|
|
478
|
-
[
|
|
479
|
-
row_index
|
|
480
|
-
for row_index in range(2, self.sheet.max_row + 1)
|
|
481
|
-
if self.sheet.cell(row_index, self.inventory_col[1]).value
|
|
482
|
-
]
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
if inventories_len > 0:
|
|
486
|
-
return
|
|
487
|
-
|
|
488
|
-
print_warning(
|
|
489
|
-
_("The remaining food wasn't read " + 'from "{0}".').format(
|
|
490
|
-
self.path,
|
|
491
|
-
)
|
|
492
|
-
)
|
|
493
|
-
|
|
494
|
-
meal_types = self.get_meal_types()
|
|
495
|
-
inventory = self.bill.spreadsheet.inventory
|
|
496
|
-
|
|
497
|
-
update_yn = False
|
|
498
|
-
|
|
499
|
-
for meal_type in meal_types:
|
|
500
|
-
bill_fpath = self.operator.get_bill_fpath(meal_type)
|
|
501
|
-
print_info(
|
|
502
|
-
_(
|
|
503
|
-
"{0} is reading remaining foods from "
|
|
504
|
-
+ 'Sheet "{1}" of Spreadsheet "{2}" ......'
|
|
505
|
-
).format(
|
|
506
|
-
app_name,
|
|
507
|
-
inventory.sheet_name,
|
|
508
|
-
bill_fpath,
|
|
509
|
-
)
|
|
510
|
-
)
|
|
511
|
-
saved_ifoods = inventory.get_save_foods(meal_type)
|
|
512
|
-
saved_ifoods_len = len(saved_ifoods)
|
|
513
|
-
if saved_ifoods_len < 1:
|
|
514
|
-
print_warning(
|
|
515
|
-
_('There is no saved inventories from "{0}"').format(
|
|
516
|
-
bill_fpath
|
|
517
|
-
)
|
|
518
|
-
)
|
|
519
|
-
return
|
|
520
|
-
|
|
521
|
-
print_warning(
|
|
522
|
-
(
|
|
523
|
-
_(
|
|
524
|
-
"Some remaining foods have been read "
|
|
525
|
-
+ 'from sheet "{0}" of spreadsheet "{1}":'
|
|
526
|
-
)
|
|
527
|
-
if len(saved_ifoods) > 1
|
|
528
|
-
else _(
|
|
529
|
-
"The remaining food has been read"
|
|
530
|
-
+ 'from sheet "{0}" of spreadsheet "{1}":'
|
|
531
|
-
)
|
|
532
|
-
).format(inventory.sheet_name, bill_fpath)
|
|
533
|
-
)
|
|
534
|
-
|
|
535
|
-
saved_ifoods_len2 = len(str(saved_ifoods_len))
|
|
536
|
-
saved_ifoods_s = sqr_slist(
|
|
537
|
-
[
|
|
538
|
-
(
|
|
539
|
-
f"({i+1:>{saved_ifoods_len2}}) "
|
|
540
|
-
+ f"{f0.name}:{f0.count} {f0.unit_name}"
|
|
541
|
-
+ f"\u2a09 {f0.unit_price:.2f} "
|
|
542
|
-
+ f"{self.bill.currency.unit}/"
|
|
543
|
-
+ f"{f0.unit_name}={f0.total_price:.2f} "
|
|
544
|
-
+ f"{self.bill.currency.unit}"
|
|
545
|
-
)
|
|
546
|
-
for i, f0 in enumerate(saved_ifoods)
|
|
547
|
-
]
|
|
548
|
-
)
|
|
549
|
-
|
|
550
|
-
saved_ifoods_s_len = max(
|
|
551
|
-
[len(s) for s in saved_ifoods_s.split("\n")]
|
|
552
|
-
)
|
|
553
|
-
saved_ifoods_info = (
|
|
554
|
-
_("Purchaser: ")
|
|
555
|
-
+ saved_ifoods[0].purchaser
|
|
556
|
-
+ "\n"
|
|
557
|
-
+ _("Inventory data: ")
|
|
558
|
-
+ saved_ifoods[0].xdate.strftime("%Y.%m.%d")
|
|
559
|
-
+ "\n"
|
|
560
|
-
)
|
|
561
|
-
print_info(saved_ifoods_s)
|
|
562
|
-
print_warning(saved_ifoods_info)
|
|
563
|
-
print_warning(
|
|
564
|
-
(
|
|
565
|
-
_('Fill them in "{0}"? (YyNn, default: "No")')
|
|
566
|
-
if len(saved_ifoods) > 1
|
|
567
|
-
else _('Fill it in "{0}"? (YyNn, default: "No")')
|
|
568
|
-
).format(self.path)
|
|
569
|
-
)
|
|
570
|
-
|
|
571
|
-
f_input = get_input().replace(" ", "")
|
|
572
|
-
if len(f_input) > 0 and f_input in "Yy":
|
|
573
|
-
update_yn = True
|
|
574
|
-
max_row = len(
|
|
575
|
-
[
|
|
576
|
-
row_index
|
|
577
|
-
for row_index in range(1, self.sheet.max_row + 1)
|
|
578
|
-
if self.sheet.cell(row_index, 1).value
|
|
579
|
-
]
|
|
580
|
-
)
|
|
581
|
-
for row_index in range(
|
|
582
|
-
max_row + 1, max_row + 1 + len(saved_ifoods)
|
|
583
|
-
):
|
|
584
|
-
f = saved_ifoods[row_index - max_row - 1]
|
|
585
|
-
self.sheet.cell(
|
|
586
|
-
row_index, self.xdate_col[1]
|
|
587
|
-
).number_format = numbers.FORMAT_TEXT
|
|
588
|
-
self.sheet.cell(
|
|
589
|
-
row_index,
|
|
590
|
-
self.xdate_col[1],
|
|
591
|
-
f.xdate.strftime("%Y-%m-%d"),
|
|
592
|
-
)
|
|
593
|
-
self.sheet.cell(
|
|
594
|
-
row_index, self.purchaser_col[1], f.purchaser
|
|
595
|
-
)
|
|
596
|
-
self.sheet.cell(row_index, self.food_name_col[1], f.name)
|
|
597
|
-
self.sheet.cell(row_index, self.food_class_col[1], f.fclass)
|
|
598
|
-
self.food_class_dv.add(
|
|
599
|
-
self.sheet.cell(row_index, self.food_class_col[1])
|
|
600
|
-
)
|
|
601
|
-
self.sheet.cell(
|
|
602
|
-
row_index,
|
|
603
|
-
self.unit_name_col[1],
|
|
604
|
-
f.unit_name,
|
|
605
|
-
)
|
|
606
|
-
self.sheet.cell(
|
|
607
|
-
row_index, self.count_col[1]
|
|
608
|
-
).number_format = numbers.FORMAT_NUMBER_00
|
|
609
|
-
self.sheet.cell(row_index, self.count_col[1], f.count)
|
|
610
|
-
self.sheet.cell(
|
|
611
|
-
row_index, self.total_price_col[1]
|
|
612
|
-
).number_format = numbers.FORMAT_NUMBER_00
|
|
613
|
-
self.sheet.cell(
|
|
614
|
-
row_index,
|
|
615
|
-
self.total_price_col[1],
|
|
616
|
-
f.total_price,
|
|
617
|
-
)
|
|
618
|
-
self.sheet.cell(row_index, self.inventory_col[1], "y")
|
|
619
|
-
|
|
620
|
-
for col_index in self.col_indexes:
|
|
621
|
-
if not col_index:
|
|
622
|
-
continue
|
|
623
|
-
self.sheet.cell(row_index, col_index).font = (
|
|
624
|
-
self.edited_cell_font
|
|
625
|
-
)
|
|
626
|
-
|
|
627
|
-
print_info(
|
|
628
|
-
(
|
|
629
|
-
_("The remaining foods have been " + 'added to "{0}".')
|
|
630
|
-
if len(saved_ifoods) > 1
|
|
631
|
-
else _('The remaining food has been added to "{0}".')
|
|
632
|
-
).format(self.path)
|
|
633
|
-
)
|
|
634
|
-
|
|
635
|
-
pass
|
|
636
|
-
|
|
637
|
-
self.wb.save(self.path)
|
|
638
|
-
self.wb.close()
|
|
639
|
-
del self.wb
|
|
640
|
-
|
|
641
|
-
if update_yn:
|
|
642
|
-
print_info(
|
|
643
|
-
(
|
|
644
|
-
_(
|
|
645
|
-
"Please check/modify the updated data. "
|
|
646
|
-
+ "(Press any key to open the file)"
|
|
647
|
-
)
|
|
648
|
-
)
|
|
649
|
-
)
|
|
650
|
-
get_input()
|
|
651
|
-
open_path(self.path)
|
|
652
|
-
print_info(
|
|
653
|
-
_("Ok, I checked it, it's ok. " + "(Press any key to continue)")
|
|
654
|
-
)
|
|
655
|
-
get_input()
|
|
656
|
-
pass
|
|
657
|
-
|
|
658
|
-
pass
|
|
659
|
-
|
|
660
|
-
def split_foods(self):
|
|
661
|
-
foods_cp = self.bill.foods.copy()
|
|
662
|
-
foods = self.bill.foods
|
|
663
|
-
|
|
664
|
-
bad_total_price_foods = []
|
|
665
|
-
for f in foods:
|
|
666
|
-
total_price_s = str(f.total_price)
|
|
667
|
-
if "." in total_price_s and len(total_price_s.split(".")[1]) > 2:
|
|
668
|
-
bad_total_price_foods.append(f)
|
|
669
|
-
if len(bad_total_price_foods) > 0:
|
|
670
|
-
bad_total_price_foods_len = len(bad_total_price_foods)
|
|
671
|
-
print_error(
|
|
672
|
-
(
|
|
673
|
-
_(
|
|
674
|
-
"The total price of the following food has more than "
|
|
675
|
-
+ "two decimal places, and {0} cannot process it."
|
|
676
|
-
)
|
|
677
|
-
if bad_total_price_foods_len == 1
|
|
678
|
-
else _(
|
|
679
|
-
"The total price of the following foods have more than "
|
|
680
|
-
+ "two decimal places, and {0} cannot process them."
|
|
681
|
-
)
|
|
682
|
-
).format(app_name)
|
|
683
|
-
)
|
|
684
|
-
bad_total_price_foods_len2 = len(str(bad_total_price_foods_len + 1))
|
|
685
|
-
print_error(
|
|
686
|
-
sqr_slist(
|
|
687
|
-
[
|
|
688
|
-
(
|
|
689
|
-
f"{i+1:>{bad_total_price_foods_len2}} "
|
|
690
|
-
+ _("{0}({1})").format(
|
|
691
|
-
f.name,
|
|
692
|
-
(
|
|
693
|
-
f"{f.xdate.year}.{f.xdate.month:0>2}"
|
|
694
|
-
+ f".{f.xdate.day:0>2}"
|
|
695
|
-
),
|
|
696
|
-
)
|
|
697
|
-
+ f" {f.total_price}"
|
|
698
|
-
)
|
|
699
|
-
for i, f in enumerate(bad_total_price_foods)
|
|
700
|
-
]
|
|
701
|
-
)
|
|
702
|
-
)
|
|
703
|
-
print_error(_("Exit."))
|
|
704
|
-
exit()
|
|
705
|
-
|
|
706
|
-
split_mode = ""
|
|
707
|
-
|
|
708
|
-
for i, f in enumerate(foods_cp):
|
|
709
|
-
up0, up1, threshold = f.count_threshold
|
|
710
|
-
f_count = f.count
|
|
711
|
-
f_total_price = f.total_price
|
|
712
|
-
f_unit_price = f.unit_price
|
|
713
|
-
if threshold:
|
|
714
|
-
if split_mode in "YyNn":
|
|
715
|
-
print_info(
|
|
716
|
-
_(
|
|
717
|
-
'The unit price of "{0}" is an '
|
|
718
|
-
+ 'infinite decimal, split "{0}"?'
|
|
719
|
-
+ '(Yes: "Y","y","".'
|
|
720
|
-
+ ' Yes for rest: "A","a".'
|
|
721
|
-
+ ' No: "N","n".'
|
|
722
|
-
+ ' No for rest: "S","s".'
|
|
723
|
-
+ ' Default: Yes for rest, "A/a".'
|
|
724
|
-
+ ")"
|
|
725
|
-
).format(f.name)
|
|
726
|
-
)
|
|
727
|
-
split_mode = get_input()
|
|
728
|
-
|
|
729
|
-
if split_mode and split_mode in "Ss":
|
|
730
|
-
return
|
|
731
|
-
|
|
732
|
-
if split_mode == "":
|
|
733
|
-
split_mode = "A"
|
|
734
|
-
|
|
735
|
-
if split_mode in "YyAa":
|
|
736
|
-
times_char = "\u2a09"
|
|
737
|
-
f0 = foods[i]
|
|
738
|
-
f0.count = threshold
|
|
739
|
-
f0.total_price = up1 * threshold
|
|
740
|
-
f0._count_threshold = None
|
|
741
|
-
|
|
742
|
-
f1_count = f_count - threshold
|
|
743
|
-
f1 = Food(
|
|
744
|
-
self.bill,
|
|
745
|
-
name=f0.name,
|
|
746
|
-
unit_name=f0.unit_name,
|
|
747
|
-
count=f1_count,
|
|
748
|
-
total_price=f1_count * up0,
|
|
749
|
-
xdate=f0.xdate,
|
|
750
|
-
purchaser=f0.purchaser,
|
|
751
|
-
fclass=f0.fclass,
|
|
752
|
-
is_inventory=f0.is_inventory,
|
|
753
|
-
is_abandoned=f0.is_abandoned,
|
|
754
|
-
meal_type=f0.meal_type,
|
|
755
|
-
)
|
|
756
|
-
|
|
757
|
-
total_price0 = round(
|
|
758
|
-
up1 * threshold + f1_count * up0, self.sd + 1
|
|
759
|
-
)
|
|
760
|
-
print_info(
|
|
761
|
-
_('"{0}" was split:').format(f0.name)
|
|
762
|
-
+ f"\n\t{f0.unit_price} "
|
|
763
|
-
+ f"{self.bill.currency.unit}"
|
|
764
|
-
+ f"/{f0.unit_name} "
|
|
765
|
-
+ f"{times_char} {f0.count} "
|
|
766
|
-
+ f"{f0.unit_name} + "
|
|
767
|
-
+ f"{f1.unit_price} "
|
|
768
|
-
+ f"{self.bill.currency.unit}"
|
|
769
|
-
+ f"/{f1.unit_name} "
|
|
770
|
-
+ f"{times_char} {f1.count} "
|
|
771
|
-
+ f"{f1.unit_name} = "
|
|
772
|
-
+ f"{total_price0} "
|
|
773
|
-
+ f"{self.bill.currency.unit} = "
|
|
774
|
-
+ f"{f_unit_price} "
|
|
775
|
-
+ f"{self.bill.currency.unit}"
|
|
776
|
-
+ f"/{f.unit_name} "
|
|
777
|
-
+ f"{times_char} {f_count} "
|
|
778
|
-
+ f"{f.unit_name} = "
|
|
779
|
-
+ f"{f_total_price}"
|
|
780
|
-
+ f"{self.bill.currency.unit}"
|
|
781
|
-
)
|
|
782
|
-
|
|
783
|
-
foods.append(f1)
|
|
784
|
-
return
|
|
785
|
-
|
|
786
|
-
def abandoned_foods_pass(self, foods):
|
|
787
|
-
ab_foods = [
|
|
788
|
-
f
|
|
789
|
-
for f in foods
|
|
790
|
-
if f.is_abandoned and not f.fclass in self.condiment_class_names
|
|
791
|
-
]
|
|
792
|
-
if len(ab_foods) < 1:
|
|
793
|
-
return True
|
|
794
|
-
print_warning(
|
|
795
|
-
(
|
|
796
|
-
_(
|
|
797
|
-
"Normally, {0} only considers non-warehoused foods as "
|
|
798
|
-
+ "seasonings, but {0} has found that the following food "
|
|
799
|
-
+ "is non-warehoused, but it is not seasonings:"
|
|
800
|
-
)
|
|
801
|
-
if len(ab_foods) == 1
|
|
802
|
-
else _(
|
|
803
|
-
"Normally, {0} only considers non-warehoused foods as "
|
|
804
|
-
+ "seasonings, but {0} has found that the following foods "
|
|
805
|
-
+ "are non-warehoused, but they are not seasonings:"
|
|
806
|
-
)
|
|
807
|
-
).format(app_name)
|
|
808
|
-
)
|
|
809
|
-
|
|
810
|
-
print_info(
|
|
811
|
-
sqr_slist([_("{0}({1})").format(f.name, f.xdate) for f in ab_foods])
|
|
812
|
-
)
|
|
813
|
-
print_error(
|
|
814
|
-
(
|
|
815
|
-
_(
|
|
816
|
-
"You may need to modify its food class, "
|
|
817
|
-
+ "otherwise {0} will go wrong."
|
|
818
|
-
)
|
|
819
|
-
if len(ab_foods) == 1
|
|
820
|
-
else _(
|
|
821
|
-
"You may need to modify their food classes, "
|
|
822
|
-
+ "otherwise {0} will go wrong."
|
|
823
|
-
)
|
|
824
|
-
).format(app_name)
|
|
825
|
-
+ _(
|
|
826
|
-
' Do you want to re-edit "{0}"? '
|
|
827
|
-
+ "(Yes: 'Y' or 'y'. Default: No.)"
|
|
828
|
-
).format(self.path)
|
|
829
|
-
)
|
|
830
|
-
re_edit_yn = get_input()
|
|
831
|
-
if re_edit_yn and re_edit_yn in "Yy":
|
|
832
|
-
open_path(self.path)
|
|
833
|
-
print_info(
|
|
834
|
-
_(
|
|
835
|
-
"Ok, I have re-edited and closed it? "
|
|
836
|
-
+ "(Press any key to continue)"
|
|
837
|
-
)
|
|
838
|
-
)
|
|
839
|
-
get_input()
|
|
840
|
-
return False
|
|
841
|
-
|
|
842
|
-
return True
|
|
843
|
-
|
|
844
|
-
pass
|
|
845
|
-
|
|
846
|
-
def read_pfoods(self):
|
|
847
|
-
self.update()
|
|
848
|
-
foods = self.pd_data
|
|
849
|
-
# if not self.abandoned_foods_pass(foods):
|
|
850
|
-
# self.pd_data = None
|
|
851
|
-
# return self.read_pfoods()
|
|
852
|
-
_foods = []
|
|
853
|
-
for __, food in foods.iterrows():
|
|
854
|
-
_food = Food(
|
|
855
|
-
self.bill,
|
|
856
|
-
name=food[self.food_name_col[0]],
|
|
857
|
-
unit_name=food[self.unit_name_col[0]],
|
|
858
|
-
count=food[self.count_col[0]],
|
|
859
|
-
total_price=food[self.total_price_col[0]],
|
|
860
|
-
xdate=food[self.xdate_col[0]],
|
|
861
|
-
purchaser=food[self.purchaser_col[0]],
|
|
862
|
-
fclass=food[self.food_class_col[0]],
|
|
863
|
-
)
|
|
864
|
-
if self.abandoned_col[0]:
|
|
865
|
-
_food.is_abandoned = not pd.isna(food[self.abandoned_col[0]])
|
|
866
|
-
if self.inventory_col[0]:
|
|
867
|
-
_food.is_inventory = not pd.isna(food[self.inventory_col[0]])
|
|
868
|
-
if self.meal_type_col and self.meal_type_col[0]:
|
|
869
|
-
_food.meal_type = food[self.meal_type_col[0]]
|
|
870
|
-
|
|
871
|
-
_foods.append(_food)
|
|
872
|
-
|
|
873
|
-
foods = _foods
|
|
874
|
-
foods = sorted(foods, key=lambda f: f.xdate)
|
|
875
|
-
self.bill.foods = foods
|
|
876
|
-
|
|
877
|
-
self.split_foods()
|
|
878
|
-
|
|
879
|
-
self.spreadsheet.preconsuming.pre_consume_foods()
|
|
880
|
-
|
|
881
|
-
return foods
|
|
882
|
-
pass
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
# The end.
|