fnschool 20250109.80531.837__py3-none-any.whl → 20251011.81638.854__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.

Files changed (278) hide show
  1. fnschoo1/__init__.py +53 -0
  2. fnschoo1/canteen/admin.py +3 -0
  3. fnschoo1/canteen/apps.py +6 -0
  4. fnschoo1/canteen/forms.py +84 -0
  5. fnschoo1/canteen/migrations/0001_initial.py +119 -0
  6. fnschoo1/canteen/migrations/0002_ingredient_is_disabled.py +20 -0
  7. fnschoo1/canteen/migrations/0003_consumption_is_disabled_alter_ingredient_is_disabled.py +23 -0
  8. fnschoo1/canteen/migrations/0004_alter_ingredient_name_category_and_more.py +66 -0
  9. fnschoo1/canteen/migrations/0005_alter_category_created_at_alter_category_name_and_more.py +41 -0
  10. fnschoo1/canteen/migrations/0006_category_is_disabled_alter_category_user_and_more.py +49 -0
  11. fnschoo1/canteen/migrations/0007_alter_consumption_amount_used_and_more.py +30 -0
  12. fnschoo1/canteen/migrations/0008_category_abbreviation_mealtype.py +67 -0
  13. fnschoo1/canteen/migrations/0009_alter_category_abbreviation_and_more.py +55 -0
  14. fnschoo1/canteen/migrations/0010_alter_consumption_options_alter_ingredient_options_and_more.py +215 -0
  15. fnschoo1/canteen/migrations/0011_category_pin_to_consumptions_top.py +23 -0
  16. fnschoo1/canteen/migrations/0012_alter_ingredient_storage_date.py +18 -0
  17. fnschoo1/canteen/models.py +179 -0
  18. fnschoo1/canteen/templates/canteen/category/create.html +17 -0
  19. fnschoo1/canteen/templates/canteen/category/delete.html +61 -0
  20. fnschoo1/canteen/templates/canteen/category/list.html +63 -0
  21. fnschoo1/canteen/templates/canteen/category/update.html +23 -0
  22. fnschoo1/canteen/templates/canteen/close.html +11 -0
  23. fnschoo1/canteen/templates/canteen/consumption/_create.html +19 -0
  24. fnschoo1/canteen/templates/canteen/consumption/create.html +456 -0
  25. fnschoo1/canteen/templates/canteen/ingredient/close.html +11 -0
  26. fnschoo1/canteen/templates/canteen/ingredient/create.html +19 -0
  27. fnschoo1/canteen/templates/canteen/ingredient/create_one.html +17 -0
  28. fnschoo1/canteen/templates/canteen/ingredient/delete.html +41 -0
  29. fnschoo1/canteen/templates/canteen/ingredient/list.html +128 -0
  30. fnschoo1/canteen/templates/canteen/ingredient/update.html +23 -0
  31. fnschoo1/canteen/templates/canteen/meal_type/create.html +17 -0
  32. fnschoo1/canteen/templates/canteen/meal_type/delete.html +56 -0
  33. fnschoo1/canteen/templates/canteen/meal_type/list.html +59 -0
  34. fnschoo1/canteen/templates/canteen/meal_type/update.html +23 -0
  35. fnschoo1/canteen/tests.py +3 -0
  36. fnschoo1/canteen/urls.py +116 -0
  37. fnschoo1/canteen/views.py +814 -0
  38. fnschoo1/canteen/workbook/generate.py +2098 -0
  39. fnschoo1/db.sqlite3 +0 -0
  40. fnschoo1/fnschool/__init__.py +13 -0
  41. fnschoo1/fnschool/asgi.py +16 -0
  42. fnschoo1/fnschool/settings.py +167 -0
  43. fnschoo1/fnschool/templatetags/fnschool_tags.py +27 -0
  44. fnschoo1/fnschool/urls.py +30 -0
  45. fnschoo1/fnschool/views.py +9 -0
  46. fnschoo1/fnschool/wsgi.py +16 -0
  47. fnschoo1/locale/en/LC_MESSAGES/django.mo +0 -0
  48. fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  49. fnschoo1/manage.py +25 -0
  50. fnschoo1/profiles/admin.py +27 -0
  51. fnschoo1/profiles/apps.py +12 -0
  52. fnschoo1/profiles/forms.py +67 -0
  53. fnschoo1/profiles/migrations/0001_initial.py +192 -0
  54. fnschoo1/profiles/migrations/0002_alter_profile_bio.py +20 -0
  55. fnschoo1/profiles/migrations/0003_alter_profile_options_alter_profile_address_and_more.py +67 -0
  56. fnschoo1/profiles/migrations/0004_profile_gender.py +26 -0
  57. fnschoo1/profiles/migrations/0005_alter_profile_gender.py +23 -0
  58. fnschoo1/profiles/models.py +60 -0
  59. fnschoo1/profiles/signals.py +20 -0
  60. fnschoo1/profiles/templates/profiles/create.html +16 -0
  61. fnschoo1/profiles/templates/profiles/detail.html +14 -0
  62. fnschoo1/profiles/templates/profiles/edit.html +12 -0
  63. fnschoo1/profiles/templates/profiles/log_in.html +20 -0
  64. fnschoo1/profiles/templates/profiles/log_out.html +12 -0
  65. fnschoo1/profiles/tests.py +3 -0
  66. fnschoo1/profiles/urls.py +15 -0
  67. fnschoo1/profiles/views.py +63 -0
  68. fnschoo1/static/css/bootstrap.min.css +11776 -0
  69. fnschoo1/static/css/fnschool.css +26 -0
  70. fnschoo1/static/images/favicon.ico +0 -0
  71. fnschoo1/static/js/bootstrap.bundle.min.js +4223 -0
  72. fnschoo1/static/js/bootstrap.min.js +2919 -0
  73. fnschoo1/static/js/fnschool.js +84 -0
  74. fnschoo1/static/js/jquery.min.js +5413 -0
  75. fnschoo1/static/js/jquery.slim.min.js +4331 -0
  76. fnschoo1/static/js/popper.min.js +1306 -0
  77. fnschoo1/static_collected/admin/css/autocomplete.css +377 -0
  78. fnschoo1/static_collected/admin/css/base.css +1224 -0
  79. fnschoo1/static_collected/admin/css/changelists.css +334 -0
  80. fnschoo1/static_collected/admin/css/dark_mode.css +136 -0
  81. fnschoo1/static_collected/admin/css/dashboard.css +30 -0
  82. fnschoo1/static_collected/admin/css/forms.css +546 -0
  83. fnschoo1/static_collected/admin/css/login.css +62 -0
  84. fnschoo1/static_collected/admin/css/nav_sidebar.css +145 -0
  85. fnschoo1/static_collected/admin/css/responsive.css +1043 -0
  86. fnschoo1/static_collected/admin/css/responsive_rtl.css +84 -0
  87. fnschoo1/static_collected/admin/css/rtl.css +311 -0
  88. fnschoo1/static_collected/admin/css/vendor/select2/select2.css +708 -0
  89. fnschoo1/static_collected/admin/css/vendor/select2/select2.min.css +1 -0
  90. fnschoo1/static_collected/admin/css/widgets.css +639 -0
  91. fnschoo1/static_collected/admin/js/SelectBox.js +128 -0
  92. fnschoo1/static_collected/admin/js/SelectFilter2.js +503 -0
  93. fnschoo1/static_collected/admin/js/actions.js +232 -0
  94. fnschoo1/static_collected/admin/js/admin/DateTimeShortcuts.js +496 -0
  95. fnschoo1/static_collected/admin/js/admin/RelatedObjectLookups.js +276 -0
  96. fnschoo1/static_collected/admin/js/autocomplete.js +33 -0
  97. fnschoo1/static_collected/admin/js/calendar.js +251 -0
  98. fnschoo1/static_collected/admin/js/cancel.js +29 -0
  99. fnschoo1/static_collected/admin/js/change_form.js +21 -0
  100. fnschoo1/static_collected/admin/js/collapse.js +43 -0
  101. fnschoo1/static_collected/admin/js/core.js +174 -0
  102. fnschoo1/static_collected/admin/js/filters.js +37 -0
  103. fnschoo1/static_collected/admin/js/inlines.js +439 -0
  104. fnschoo1/static_collected/admin/js/jquery.init.js +8 -0
  105. fnschoo1/static_collected/admin/js/nav_sidebar.js +81 -0
  106. fnschoo1/static_collected/admin/js/popup_response.js +24 -0
  107. fnschoo1/static_collected/admin/js/prepopulate.js +43 -0
  108. fnschoo1/static_collected/admin/js/prepopulate_init.js +20 -0
  109. fnschoo1/static_collected/admin/js/theme.js +57 -0
  110. fnschoo1/static_collected/admin/js/urlify.js +529 -0
  111. fnschoo1/static_collected/admin/js/vendor/jquery/jquery.js +10913 -0
  112. fnschoo1/static_collected/admin/js/vendor/jquery/jquery.min.js +2 -0
  113. fnschoo1/static_collected/admin/js/vendor/select2/i18n/af.js +43 -0
  114. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ar.js +36 -0
  115. fnschoo1/static_collected/admin/js/vendor/select2/i18n/az.js +33 -0
  116. fnschoo1/static_collected/admin/js/vendor/select2/i18n/bg.js +38 -0
  117. fnschoo1/static_collected/admin/js/vendor/select2/i18n/bn.js +39 -0
  118. fnschoo1/static_collected/admin/js/vendor/select2/i18n/bs.js +48 -0
  119. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ca.js +41 -0
  120. fnschoo1/static_collected/admin/js/vendor/select2/i18n/cs.js +62 -0
  121. fnschoo1/static_collected/admin/js/vendor/select2/i18n/da.js +37 -0
  122. fnschoo1/static_collected/admin/js/vendor/select2/i18n/de.js +41 -0
  123. fnschoo1/static_collected/admin/js/vendor/select2/i18n/dsb.js +51 -0
  124. fnschoo1/static_collected/admin/js/vendor/select2/i18n/el.js +43 -0
  125. fnschoo1/static_collected/admin/js/vendor/select2/i18n/en.js +41 -0
  126. fnschoo1/static_collected/admin/js/vendor/select2/i18n/es.js +41 -0
  127. fnschoo1/static_collected/admin/js/vendor/select2/i18n/et.js +38 -0
  128. fnschoo1/static_collected/admin/js/vendor/select2/i18n/eu.js +45 -0
  129. fnschoo1/static_collected/admin/js/vendor/select2/i18n/fa.js +42 -0
  130. fnschoo1/static_collected/admin/js/vendor/select2/i18n/fi.js +42 -0
  131. fnschoo1/static_collected/admin/js/vendor/select2/i18n/fr.js +43 -0
  132. fnschoo1/static_collected/admin/js/vendor/select2/i18n/gl.js +40 -0
  133. fnschoo1/static_collected/admin/js/vendor/select2/i18n/he.js +44 -0
  134. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hi.js +40 -0
  135. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hr.js +45 -0
  136. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hsb.js +51 -0
  137. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hu.js +44 -0
  138. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hy.js +40 -0
  139. fnschoo1/static_collected/admin/js/vendor/select2/i18n/id.js +36 -0
  140. fnschoo1/static_collected/admin/js/vendor/select2/i18n/is.js +37 -0
  141. fnschoo1/static_collected/admin/js/vendor/select2/i18n/it.js +43 -0
  142. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ja.js +40 -0
  143. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ka.js +42 -0
  144. fnschoo1/static_collected/admin/js/vendor/select2/i18n/km.js +38 -0
  145. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ko.js +42 -0
  146. fnschoo1/static_collected/admin/js/vendor/select2/i18n/lt.js +45 -0
  147. fnschoo1/static_collected/admin/js/vendor/select2/i18n/lv.js +41 -0
  148. fnschoo1/static_collected/admin/js/vendor/select2/i18n/mk.js +42 -0
  149. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ms.js +38 -0
  150. fnschoo1/static_collected/admin/js/vendor/select2/i18n/nb.js +38 -0
  151. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ne.js +44 -0
  152. fnschoo1/static_collected/admin/js/vendor/select2/i18n/nl.js +46 -0
  153. fnschoo1/static_collected/admin/js/vendor/select2/i18n/pl.js +43 -0
  154. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ps.js +41 -0
  155. fnschoo1/static_collected/admin/js/vendor/select2/i18n/pt-BR.js +39 -0
  156. fnschoo1/static_collected/admin/js/vendor/select2/i18n/pt.js +41 -0
  157. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ro.js +43 -0
  158. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ru.js +48 -0
  159. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sk.js +61 -0
  160. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sl.js +41 -0
  161. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sq.js +43 -0
  162. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sr-Cyrl.js +48 -0
  163. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sr.js +48 -0
  164. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sv.js +40 -0
  165. fnschoo1/static_collected/admin/js/vendor/select2/i18n/th.js +36 -0
  166. fnschoo1/static_collected/admin/js/vendor/select2/i18n/tk.js +36 -0
  167. fnschoo1/static_collected/admin/js/vendor/select2/i18n/tr.js +40 -0
  168. fnschoo1/static_collected/admin/js/vendor/select2/i18n/uk.js +59 -0
  169. fnschoo1/static_collected/admin/js/vendor/select2/i18n/vi.js +37 -0
  170. fnschoo1/static_collected/admin/js/vendor/select2/i18n/zh-CN.js +36 -0
  171. fnschoo1/static_collected/admin/js/vendor/select2/i18n/zh-TW.js +33 -0
  172. fnschoo1/static_collected/admin/js/vendor/select2/select2.full.js +7115 -0
  173. fnschoo1/static_collected/admin/js/vendor/select2/select2.full.min.js +2 -0
  174. fnschoo1/static_collected/admin/js/vendor/xregexp/xregexp.js +4993 -0
  175. fnschoo1/static_collected/admin/js/vendor/xregexp/xregexp.min.js +160 -0
  176. fnschoo1/static_collected/css/bootstrap.min.css +11776 -0
  177. fnschoo1/static_collected/css/fnschool.css +26 -0
  178. fnschoo1/static_collected/images/favicon.ico +0 -0
  179. fnschoo1/static_collected/js/bootstrap.bundle.min.js +4223 -0
  180. fnschoo1/static_collected/js/bootstrap.min.js +2919 -0
  181. fnschoo1/static_collected/js/fnschool.js +84 -0
  182. fnschoo1/static_collected/js/jquery.min.js +5413 -0
  183. fnschoo1/static_collected/js/jquery.slim.min.js +4331 -0
  184. fnschoo1/static_collected/js/popper.min.js +1306 -0
  185. fnschoo1/templates/base/_css.html +1 -0
  186. fnschoo1/templates/base/_js.html +15 -0
  187. fnschoo1/templates/base/content.html +30 -0
  188. fnschoo1/templates/base/header_content_footer.html +35 -0
  189. fnschoo1/templates/close.html +11 -0
  190. fnschoo1/templates/home.html +51 -0
  191. fnschoo1/templates/includes/_footer.html +39 -0
  192. fnschoo1/templates/includes/_header.html +77 -0
  193. fnschoo1/templates/includes/_navigation.html +0 -0
  194. fnschoo1/templates/includes/_paginator.html +27 -0
  195. fnschoo1/templates/registration/logged_out.html +0 -0
  196. fnschoo1/templates/registration/login.html +0 -0
  197. fnschool-20251011.81638.854.dist-info/METADATA +179 -0
  198. fnschool-20251011.81638.854.dist-info/RECORD +209 -0
  199. {fnschool-20250109.80531.837.dist-info → fnschool-20251011.81638.854.dist-info}/WHEEL +1 -1
  200. fnschool-20251011.81638.854.dist-info/entry_points.txt +2 -0
  201. fnschool-20251011.81638.854.dist-info/top_level.txt +1 -0
  202. fnschool/__init__.py +0 -35
  203. fnschool/__main__.py +0 -16
  204. fnschool/app.py +0 -103
  205. fnschool/canteen/__init__.py +0 -3
  206. fnschool/canteen/__main__.py +0 -3
  207. fnschool/canteen/bill.py +0 -253
  208. fnschool/canteen/canteen.py +0 -1
  209. fnschool/canteen/canteen.toml +0 -61
  210. fnschool/canteen/config.py +0 -10
  211. fnschool/canteen/consuming.py +0 -53
  212. fnschool/canteen/currency.py +0 -17
  213. fnschool/canteen/data/bill.i18n.xlsx +0 -0
  214. fnschool/canteen/data/bill.xlsx +0 -0
  215. fnschool/canteen/data/consuming.xlsx +0 -0
  216. fnschool/canteen/data/purchase_list.xlsx +0 -0
  217. fnschool/canteen/entry.py +0 -40
  218. fnschool/canteen/food.py +0 -206
  219. fnschool/canteen/food_classes.py +0 -33
  220. fnschool/canteen/food_classes.toml +0 -64
  221. fnschool/canteen/operator.py +0 -91
  222. fnschool/canteen/path.py +0 -28
  223. fnschool/canteen/spreadsheet/base.py +0 -213
  224. fnschool/canteen/spreadsheet/consuming.py +0 -310
  225. fnschool/canteen/spreadsheet/consumingsum.py +0 -76
  226. fnschool/canteen/spreadsheet/cover.py +0 -64
  227. fnschool/canteen/spreadsheet/ctspreadsheet.py +0 -351
  228. fnschool/canteen/spreadsheet/food.py +0 -680
  229. fnschool/canteen/spreadsheet/inventory.py +0 -375
  230. fnschool/canteen/spreadsheet/merging.py +0 -340
  231. fnschool/canteen/spreadsheet/preconsuming.py +0 -329
  232. fnschool/canteen/spreadsheet/purchasing.py +0 -885
  233. fnschool/canteen/spreadsheet/purchasingsum.py +0 -110
  234. fnschool/canteen/spreadsheet/spreadsheet.py +0 -363
  235. fnschool/canteen/spreadsheet/translating.py +0 -12
  236. fnschool/canteen/spreadsheet/unwarehousing.py +0 -178
  237. fnschool/canteen/spreadsheet/unwarehousingsum.py +0 -11
  238. fnschool/canteen/spreadsheet/warehousing.py +0 -360
  239. fnschool/canteen/spreadsheet/workbook.py +0 -17
  240. fnschool/canteen/test.py +0 -97
  241. fnschool/config.py +0 -48
  242. fnschool/entry.py +0 -67
  243. fnschool/exam/__init__.py +0 -8
  244. fnschool/exam/data/parental_emails.xlsx +0 -0
  245. fnschool/exam/data/score.xlsx +0 -0
  246. fnschool/exam/email.py +0 -349
  247. fnschool/exam/entry.py +0 -36
  248. fnschool/exam/language.py +0 -19
  249. fnschool/exam/path.py +0 -24
  250. fnschool/exam/score.py +0 -1191
  251. fnschool/exam/subject.py +0 -20
  252. fnschool/exam/teacher.py +0 -54
  253. fnschool/external.py +0 -89
  254. fnschool/games/__init__.py +0 -1
  255. fnschool/games/__main__.py +0 -1
  256. fnschool/games/games.py +0 -1
  257. fnschool/inoutput.py +0 -97
  258. fnschool/language.py +0 -40
  259. fnschool/locales/en_US/LC_MESSAGES/fnschool.mo +0 -0
  260. fnschool/locales/zh_CN/LC_MESSAGES/fnschool.mo +0 -0
  261. fnschool/locales/zh_HK/LC_MESSAGES/fnschool.mo +0 -0
  262. fnschool/locales/zh_SG/LC_MESSAGES/fnschool.mo +0 -0
  263. fnschool/locales/zh_TW/LC_MESSAGES/fnschool.mo +0 -0
  264. fnschool/path.py +0 -45
  265. fnschool/test.py +0 -24
  266. fnschool/user.py +0 -262
  267. fnschool-20250109.80531.837.dist-info/METADATA +0 -342
  268. fnschool-20250109.80531.837.dist-info/RECORD +0 -78
  269. fnschool-20250109.80531.837.dist-info/entry_points.txt +0 -5
  270. fnschool-20250109.80531.837.dist-info/top_level.txt +0 -1
  271. /fnschool/canteen/consume.py → /fnschoo1/canteen/__init__.py +0 -0
  272. /fnschool/canteen/food_recount.toml → /fnschoo1/canteen/migrations/__init__.py +0 -0
  273. {fnschool/canteen/spreadsheet → fnschoo1/canteen/workbook}/__init__.py +0 -0
  274. /fnschool/exam/__main__.py → /fnschoo1/fnschool/templatetags/__init__.py +0 -0
  275. /fnschool/canteen/food_recounts.toml → /fnschoo1/profiles/__init__.py +0 -0
  276. /fnschool/canteen/warehouse.py → /fnschoo1/profiles/migrations/__init__.py +0 -0
  277. /fnschool/canteen/workbook.toml → /fnschoo1/templates/base/_content.html +0 -0
  278. {fnschool-20250109.80531.837.dist-info → fnschool-20251011.81638.854.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,215 @@
1
+ # Generated by Django 4.2.25 on 2025-10-08 16:46
2
+
3
+ import django.core.validators
4
+ import django.db.models.deletion
5
+ from django.conf import settings
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ dependencies = [
12
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13
+ ("canteen", "0009_alter_category_abbreviation_and_more"),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.AlterModelOptions(
18
+ name="consumption",
19
+ options={
20
+ "ordering": ["-date_of_using"],
21
+ "verbose_name": "Consumption Record",
22
+ "verbose_name_plural": "Consumption Records",
23
+ },
24
+ ),
25
+ migrations.AlterModelOptions(
26
+ name="ingredient",
27
+ options={
28
+ "verbose_name": "Ingredient",
29
+ "verbose_name_plural": "Ingredient List",
30
+ },
31
+ ),
32
+ migrations.AlterField(
33
+ model_name="category",
34
+ name="abbreviation",
35
+ field=models.CharField(
36
+ blank=True,
37
+ max_length=100,
38
+ null=True,
39
+ verbose_name="abbreviation",
40
+ ),
41
+ ),
42
+ migrations.AlterField(
43
+ model_name="category",
44
+ name="created_at",
45
+ field=models.DateField(verbose_name="Creating Date"),
46
+ ),
47
+ migrations.AlterField(
48
+ model_name="category",
49
+ name="is_disabled",
50
+ field=models.BooleanField(
51
+ default=False, verbose_name="Is Disabled"
52
+ ),
53
+ ),
54
+ migrations.AlterField(
55
+ model_name="category",
56
+ name="name",
57
+ field=models.CharField(
58
+ max_length=100, verbose_name="Category name"
59
+ ),
60
+ ),
61
+ migrations.AlterField(
62
+ model_name="category",
63
+ name="user",
64
+ field=models.ForeignKey(
65
+ on_delete=django.db.models.deletion.CASCADE,
66
+ related_name="Categories",
67
+ to=settings.AUTH_USER_MODEL,
68
+ verbose_name="Ingredient category",
69
+ ),
70
+ ),
71
+ migrations.AlterField(
72
+ model_name="consumption",
73
+ name="date_of_using",
74
+ field=models.DateField(verbose_name="Date"),
75
+ ),
76
+ migrations.AlterField(
77
+ model_name="consumption",
78
+ name="ingredient",
79
+ field=models.ForeignKey(
80
+ on_delete=django.db.models.deletion.CASCADE,
81
+ related_name="consumptions",
82
+ to="canteen.ingredient",
83
+ verbose_name="Ingredient",
84
+ ),
85
+ ),
86
+ migrations.AlterField(
87
+ model_name="consumption",
88
+ name="is_disabled",
89
+ field=models.BooleanField(
90
+ default=False, verbose_name="Is Disabled"
91
+ ),
92
+ ),
93
+ migrations.AlterField(
94
+ model_name="ingredient",
95
+ name="category",
96
+ field=models.ForeignKey(
97
+ on_delete=django.db.models.deletion.PROTECT,
98
+ related_name="ingredients",
99
+ to="canteen.category",
100
+ verbose_name="Ingredient Category",
101
+ ),
102
+ ),
103
+ migrations.AlterField(
104
+ model_name="ingredient",
105
+ name="is_disabled",
106
+ field=models.BooleanField(
107
+ default=False, verbose_name="Is Ingredient Disabled"
108
+ ),
109
+ ),
110
+ migrations.AlterField(
111
+ model_name="ingredient",
112
+ name="is_ignorable",
113
+ field=models.BooleanField(
114
+ default=False, verbose_name="Is Ingredient Ignorable"
115
+ ),
116
+ ),
117
+ migrations.AlterField(
118
+ model_name="ingredient",
119
+ name="meal_type",
120
+ field=models.ForeignKey(
121
+ on_delete=django.db.models.deletion.PROTECT,
122
+ related_name="ingredients",
123
+ to="canteen.mealtype",
124
+ verbose_name="Ingredient Meal Type",
125
+ ),
126
+ ),
127
+ migrations.AlterField(
128
+ model_name="ingredient",
129
+ name="name",
130
+ field=models.CharField(
131
+ max_length=100, verbose_name="Ingredient Name"
132
+ ),
133
+ ),
134
+ migrations.AlterField(
135
+ model_name="ingredient",
136
+ name="quantity",
137
+ field=models.IntegerField(
138
+ validators=[django.core.validators.MinValueValidator(0)],
139
+ verbose_name="Ingredient Quantity",
140
+ ),
141
+ ),
142
+ migrations.AlterField(
143
+ model_name="ingredient",
144
+ name="quantity_unit_name",
145
+ field=models.CharField(
146
+ blank=True,
147
+ max_length=20,
148
+ null=True,
149
+ verbose_name="Unit Name of Ingredient Quantity",
150
+ ),
151
+ ),
152
+ migrations.AlterField(
153
+ model_name="ingredient",
154
+ name="storage_date",
155
+ field=models.DateField(verbose_name="Storage Date"),
156
+ ),
157
+ migrations.AlterField(
158
+ model_name="ingredient",
159
+ name="total_price",
160
+ field=models.DecimalField(
161
+ decimal_places=2,
162
+ max_digits=10,
163
+ verbose_name="Ingredient Total Price",
164
+ ),
165
+ ),
166
+ migrations.AlterField(
167
+ model_name="ingredient",
168
+ name="user",
169
+ field=models.ForeignKey(
170
+ on_delete=django.db.models.deletion.CASCADE,
171
+ related_name="ingredients",
172
+ to=settings.AUTH_USER_MODEL,
173
+ verbose_name="User",
174
+ ),
175
+ ),
176
+ migrations.AlterField(
177
+ model_name="mealtype",
178
+ name="abbreviation",
179
+ field=models.CharField(
180
+ blank=True,
181
+ max_length=100,
182
+ null=True,
183
+ verbose_name="Abbreviation",
184
+ ),
185
+ ),
186
+ migrations.AlterField(
187
+ model_name="mealtype",
188
+ name="created_at",
189
+ field=models.DateField(verbose_name="Creating Date"),
190
+ ),
191
+ migrations.AlterField(
192
+ model_name="mealtype",
193
+ name="is_disabled",
194
+ field=models.BooleanField(
195
+ default=False, verbose_name="Is Disabled"
196
+ ),
197
+ ),
198
+ migrations.AlterField(
199
+ model_name="mealtype",
200
+ name="name",
201
+ field=models.CharField(
202
+ max_length=100, verbose_name="Meal type name"
203
+ ),
204
+ ),
205
+ migrations.AlterField(
206
+ model_name="mealtype",
207
+ name="user",
208
+ field=models.ForeignKey(
209
+ on_delete=django.db.models.deletion.CASCADE,
210
+ related_name="meal_types",
211
+ to=settings.AUTH_USER_MODEL,
212
+ verbose_name="Ingredient meal type",
213
+ ),
214
+ ),
215
+ ]
@@ -0,0 +1,23 @@
1
+ # Generated by Django 4.2.25 on 2025-10-09 09:30
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ (
10
+ "canteen",
11
+ "0010_alter_consumption_options_alter_ingredient_options_and_more",
12
+ ),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AddField(
17
+ model_name="category",
18
+ name="pin_to_consumptions_top",
19
+ field=models.BooleanField(
20
+ default=False, verbose_name="Pin to Consumptions Top"
21
+ ),
22
+ ),
23
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.25 on 2025-10-10 04:39
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("canteen", "0011_category_pin_to_consumptions_top"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="ingredient",
15
+ name="storage_date",
16
+ field=models.DateField(null=True, verbose_name="Storage Date"),
17
+ ),
18
+ ]
@@ -0,0 +1,179 @@
1
+ import re
2
+ from decimal import Decimal
3
+
4
+ from django.conf import settings
5
+ from django.core.validators import MinValueValidator
6
+
7
+ # Create your models here.
8
+ from django.db import models
9
+ from django.db.models import Q
10
+ from fnschool import _
11
+
12
+
13
+ class MealType(models.Model):
14
+ user = models.ForeignKey(
15
+ to=settings.AUTH_USER_MODEL,
16
+ on_delete=models.CASCADE,
17
+ related_name="meal_types",
18
+ verbose_name=_("Ingredient meal type"),
19
+ )
20
+ name = models.CharField(max_length=100, verbose_name=_("Meal type name"))
21
+ abbreviation = models.CharField(
22
+ null=True, blank=True, max_length=100, verbose_name=_("Abbreviation")
23
+ )
24
+ created_at = models.DateField(verbose_name=_("Creating Date"))
25
+ is_disabled = models.BooleanField(
26
+ default=False, verbose_name=_("Is Disabled")
27
+ )
28
+
29
+ def __str__(self):
30
+ return self.abbreviation or self.name
31
+
32
+
33
+ class Category(models.Model):
34
+ user = models.ForeignKey(
35
+ to=settings.AUTH_USER_MODEL,
36
+ on_delete=models.CASCADE,
37
+ related_name="Categories",
38
+ verbose_name=_("Ingredient category"),
39
+ )
40
+ name = models.CharField(max_length=100, verbose_name=_("Category name"))
41
+ abbreviation = models.CharField(
42
+ null=True, blank=True, max_length=100, verbose_name=_("abbreviation")
43
+ )
44
+ created_at = models.DateField(verbose_name=_("Creating Date"))
45
+ is_disabled = models.BooleanField(
46
+ default=False, verbose_name=_("Is Disabled")
47
+ )
48
+ pin_to_consumptions_top = models.BooleanField(
49
+ default=False, verbose_name=_("Pin to Consumptions Top")
50
+ )
51
+
52
+ def __str__(self):
53
+ return self.name
54
+
55
+
56
+ class Ingredient(models.Model):
57
+
58
+ user = models.ForeignKey(
59
+ to=settings.AUTH_USER_MODEL,
60
+ on_delete=models.CASCADE,
61
+ related_name="ingredients",
62
+ verbose_name=_("User"),
63
+ )
64
+ storage_date = models.DateField(verbose_name=_("Storage Date"))
65
+ name = models.CharField(max_length=100, verbose_name=_("Ingredient Name"))
66
+ meal_type = models.ForeignKey(
67
+ MealType,
68
+ on_delete=models.PROTECT,
69
+ related_name="ingredients",
70
+ verbose_name=_("Ingredient Meal Type"),
71
+ )
72
+ category = models.ForeignKey(
73
+ Category,
74
+ on_delete=models.PROTECT,
75
+ related_name="ingredients",
76
+ verbose_name=_("Ingredient Category"),
77
+ )
78
+ quantity = models.IntegerField(
79
+ validators=[
80
+ MinValueValidator(0),
81
+ ],
82
+ verbose_name=_("Ingredient Quantity"),
83
+ )
84
+ quantity_unit_name = models.CharField(
85
+ max_length=20,
86
+ null=True,
87
+ blank=True,
88
+ verbose_name=_("Unit Name of Ingredient Quantity"),
89
+ )
90
+
91
+ total_price = models.DecimalField(
92
+ max_digits=10,
93
+ decimal_places=2,
94
+ verbose_name=_("Ingredient Total Price"),
95
+ )
96
+
97
+ is_ignorable = models.BooleanField(
98
+ default=False, verbose_name=_("Is Ingredient Ignorable")
99
+ )
100
+ is_disabled = models.BooleanField(
101
+ default=False, verbose_name=_("Is Ingredient Disabled")
102
+ )
103
+
104
+ @property
105
+ def unit_price(self):
106
+ if self.quantity > 0:
107
+ return self.total_price / self.quantity
108
+ return 0
109
+
110
+ @property
111
+ def cleaned_consumptions(self):
112
+ consumptions = self.consumptions.all()
113
+ for c1 in consumptions:
114
+ for c2 in consumptions:
115
+ if c1.date_of_using == c2.date_of_using and c1.id != c2.id:
116
+ c1.is_disabled = False
117
+ c1.save()
118
+ c2.delete()
119
+ return consumptions
120
+
121
+ @property
122
+ def consuming_quantity(self):
123
+ consumptions = self.cleaned_consumptions
124
+ if not consumptions:
125
+ return 0
126
+ quantity = sum(
127
+ [c.amount_used for c in consumptions if not c.is_disabled]
128
+ )
129
+ return quantity
130
+
131
+ @property
132
+ def remaining_quantity(self):
133
+ consumptions = self.cleaned_consumptions
134
+ if not consumptions:
135
+ return self.quantity
136
+ quantity = self.quantity - sum(
137
+ [c.amount_used for c in consumptions if not c.is_disabled]
138
+ )
139
+ return quantity
140
+
141
+ class Meta:
142
+ verbose_name = "Ingredient"
143
+ verbose_name_plural = _("Ingredient List")
144
+
145
+ def __str__(self):
146
+ return f"{self.name} ({self.storage_date})"
147
+
148
+
149
+ class Consumption(models.Model):
150
+ ingredient = models.ForeignKey(
151
+ Ingredient,
152
+ on_delete=models.CASCADE,
153
+ related_name="consumptions",
154
+ verbose_name=_("Ingredient"),
155
+ )
156
+
157
+ date_of_using = models.DateField(verbose_name=_("Date"))
158
+ amount_used = models.IntegerField(
159
+ verbose_name="消耗数量",
160
+ validators=[MinValueValidator(0)],
161
+ )
162
+ is_disabled = models.BooleanField(
163
+ default=False, verbose_name=_("Is Disabled")
164
+ )
165
+
166
+ class Meta:
167
+ verbose_name = _("Consumption Record")
168
+ verbose_name_plural = _("Consumption Records")
169
+ ordering = ["-date_of_using"]
170
+
171
+ def __str__(self):
172
+ return _("{0} of {1} was consumed on {2} .").format(
173
+ str(self.amount_used) + self.ingredient.quantity_unit_name,
174
+ self.ingredient.name,
175
+ self.date_of_using,
176
+ )
177
+
178
+
179
+ # The end.
@@ -0,0 +1,17 @@
1
+ {% extends "base/content.html" %}
2
+ {% load i18n %}
3
+ {% load crispy_forms_tags %}
4
+ {% block title %}
5
+ {% trans "New Category" %}
6
+ {% endblock %}
7
+ {% block content %}
8
+ <h1>{% trans "Create new category" %}</h1>
9
+ <form method="post" action="{% url 'canteen:create_category' %}">
10
+ {% csrf_token %}
11
+ {{ form|crispy }}
12
+ <div class="py-3 fixed-bottom text-end bg-dark">
13
+ <button type="submit" class="btn btn-primary">{% trans "Save" %}</button>
14
+ <button type="button" class="btn btn-danger" onclick="window.close();">{% trans "Cancel" %}</button>
15
+ </div>
16
+ </form>
17
+ {% endblock %}
@@ -0,0 +1,61 @@
1
+ {% extends "base/content.html" %}
2
+ {% load crispy_forms_tags %}
3
+ {% block title %}
4
+ {% blocktrans with instance_name=category.name %}
5
+ Delete category {{ instance_name }}
6
+ {% endblocktrans %}
7
+ {% endblock %}
8
+ {% block content %}
9
+ <h5 class="">
10
+ {% blocktrans with instance_name=category.name %}
11
+ Delete category {{ instance_name }} .
12
+ {% endblocktrans %}
13
+ </h5>
14
+ <p>
15
+ <dl>
16
+ <dt>{% trans 'Category Name' %}</dt>
17
+ <dd>
18
+ {{ category.name }}
19
+ </dd>
20
+ <dt>{% trans 'Created at' %}</dt>
21
+ <dd>
22
+ {{ category.created_at }}
23
+ </dd>
24
+ <dt>{% trans 'Pin to consumptions top' %}</dt>
25
+ <dd>
26
+ {% if category.pin_to_consumptions_top %}
27
+ {% trans "Yes" %}
28
+ {% else %}
29
+ {% trans "No" %}
30
+ {% endif %}
31
+ </dd>
32
+
33
+ <dt>{% trans 'Is Disabled' %}</dt>
34
+ <dd>
35
+ {% if category.is_disabled %}
36
+ {% trans "Yes" %}
37
+ {% else %}
38
+ {% trans "No" %}
39
+ {% endif %}
40
+ </dd>
41
+ </dl>
42
+ </p>
43
+ <p class="text-danger">
44
+ {% blocktrans %}
45
+ Are you sure you want to delete this category? This operation is irreversible.
46
+ {% endblocktrans%}
47
+ </p>
48
+ <div class="py-3 fixed-bottom text-end bg-dark">
49
+ <form method="post"
50
+ action="{% url 'canteen:delete_category' category.pk %}"
51
+ style="display: inline">
52
+ {% csrf_token %}
53
+ <button type="submit" class="btn btn-danger">{% trans 'Delete' %}</button>
54
+ <button type="button" class="btn btn-primary" onclick="window.close();">{% trans "Cancel" %}</button>
55
+ </form>
56
+ </div>
57
+ <script>
58
+ const elements = document.querySelectorAll('dd');
59
+ localize_content("dd")
60
+ </script>
61
+ {% endblock %}
@@ -0,0 +1,63 @@
1
+ {% extends "base/header_content_footer.html" %}
2
+ {% load crispy_forms_tags %}
3
+ {% block title %}
4
+ {% trans "Category list" %}
5
+ {% endblock %}
6
+ {% block content %}
7
+ <h1>{% trans "Category list" %}</h1>
8
+ <hr />
9
+ <div class="d-flex justify-content-end pt-2">
10
+ <a class="btn btn-primary"
11
+ onclick="open_small_window('{% url 'canteen:create_category' %}'); return false;">{% trans "Add" %}</a>
12
+ </div>
13
+ <table class="table table-bordered table-striped table-hover table-condensed">
14
+ <thead>
15
+ <tr>
16
+ <th>#</th>
17
+ <th>{% trans "Category Name" %}</th>
18
+ <th>{% trans "Abbreviation" %}</th>
19
+ <th>{% trans "Created at" %}</th>
20
+ <th>{% trans "Pin to consumptions top" %}</th>
21
+ <th>{% trans "Is Disabled" %}</th>
22
+ <th>#</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ {% for category in categories %}
27
+ <tr>
28
+ <td>{{ forloop.counter }}</td>
29
+ <td>{{ category.name }}</td>
30
+ <td>{{ category.abbreviation }}</td>
31
+ <td>{{ category.created_at|date:"Y-m-d" }}</td>
32
+ <td>
33
+ {% if category.pin_to_consumptions_top %}
34
+ {% trans "Yes" %}
35
+ {% endif %}
36
+ </td>
37
+ <td>
38
+ {% if category.is_disabled %}
39
+ {% trans "Yes" %}
40
+ {% endif %}
41
+ </td>
42
+ <td>
43
+ <a class="text-primary"
44
+ target="_blank"
45
+ onclick="open_small_window('{% url 'canteen:update_category' category.id %}'); return false; ">{% trans "Edit" %}</a>
46
+ |
47
+ <a class="text-danger"
48
+ onclick="open_small_window('{% url 'canteen:delete_category' category.id %}'); return false; "
49
+ target="_blank">{% trans "Delete" %}</a>
50
+ </td>
51
+ </tr>
52
+ {% empty %}
53
+ <tr>
54
+ <td colspan="2">{% trans "No categories yet." %}</td>
55
+ </tr>
56
+ {% endfor %}
57
+ </tbody>
58
+ </table>
59
+ <script>
60
+ localize_content($("td"))
61
+ </script>
62
+ {% include "includes/_paginator.html" %}
63
+ {% endblock %}
@@ -0,0 +1,23 @@
1
+ {% extends "base/content.html" %}
2
+ {% load crispy_forms_tags %}
3
+ {% block title %}
4
+ {% blocktrans with instance_name=form.instance.name%}
5
+ Edit Category {{ instance_name }}
6
+ {% endblocktrans %}
7
+ {% endblock %}
8
+ {% block content %}
9
+ <div class="">
10
+ <h5 class="">
11
+ {% blocktrans with instance_name=form.instance.name%}
12
+ Edit Category {{ instance_name }} .{% endblocktrans %}
13
+ </h5>
14
+ </div>
15
+ <form method="post"
16
+ action="{% url 'canteen:update_category' form.instance.id %}">
17
+ {% csrf_token %} {{ form|crispy }}
18
+ <div class="py-3 fixed-bottom text-end bg-dark">
19
+ <button type="submit" class="btn btn-primary">{% trans "Save" %}</button>
20
+ <button type="button" class="btn btn-danger" onclick="window.close();">{% trans "Cancel" %}</button>
21
+ </div>
22
+ </form>
23
+ {% endblock %}
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+ <script>
5
+ if (window.opener && !window.opener.closed) {
6
+ window.opener.location.reload();
7
+ }
8
+ window.close();
9
+ </script>
10
+ </body>
11
+ </html>
@@ -0,0 +1,19 @@
1
+ {% load i18n %}
2
+ {% load crispy_forms_tags %}
3
+ {% for form in form_list %}
4
+ <td class="date-{{ form.instance.date_of_using|date:'Y-m-d' }}"
5
+ data-date_of_using="{{ form.instance.date_of_using|date:'Y-m-d' }}">
6
+ <form onkeydown="if(event.keyCode==13){return false;}"
7
+ name="consumption_form"
8
+ data-is_disabled="{{ form.instance.is_disabled }}"
9
+ data-amount_used="{{ form.instance.amount_used }}"
10
+ data-ingredient_unit_price="{{ form.instance.ingredient.unit_price }}"
11
+ data-date_of_using="{{ form.instance.date_of_using|date:"Y-m-d" }}"
12
+ data-ingredient_meal_type="{{ form.instance.ingredient.meal_type }}"
13
+ {% if form.instance.id %} action="{% url 'canteen:new_consumption' form.instance.id %}" {% else %} action="{% url 'canteen:new_consumption' %}" {% endif %}
14
+ data-ingredient-id="{{ form.instance.ingredient.id }}">
15
+ {% csrf_token %}
16
+ {% for field in form %}{{ field }}{% endfor %}
17
+ </form>
18
+ </td>
19
+ {% endfor %}