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.

Files changed (276) hide show
  1. fnschoo1/__init__.py +52 -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/js/bootstrap.bundle.min.js +4223 -0
  71. fnschoo1/static/js/bootstrap.min.js +2919 -0
  72. fnschoo1/static/js/fnschool.js +84 -0
  73. fnschoo1/static/js/jquery.min.js +5413 -0
  74. fnschoo1/static/js/jquery.slim.min.js +4331 -0
  75. fnschoo1/static/js/popper.min.js +1306 -0
  76. fnschoo1/static_collected/admin/css/autocomplete.css +377 -0
  77. fnschoo1/static_collected/admin/css/base.css +1224 -0
  78. fnschoo1/static_collected/admin/css/changelists.css +334 -0
  79. fnschoo1/static_collected/admin/css/dark_mode.css +136 -0
  80. fnschoo1/static_collected/admin/css/dashboard.css +30 -0
  81. fnschoo1/static_collected/admin/css/forms.css +546 -0
  82. fnschoo1/static_collected/admin/css/login.css +62 -0
  83. fnschoo1/static_collected/admin/css/nav_sidebar.css +145 -0
  84. fnschoo1/static_collected/admin/css/responsive.css +1043 -0
  85. fnschoo1/static_collected/admin/css/responsive_rtl.css +84 -0
  86. fnschoo1/static_collected/admin/css/rtl.css +311 -0
  87. fnschoo1/static_collected/admin/css/vendor/select2/select2.css +708 -0
  88. fnschoo1/static_collected/admin/css/vendor/select2/select2.min.css +1 -0
  89. fnschoo1/static_collected/admin/css/widgets.css +639 -0
  90. fnschoo1/static_collected/admin/js/SelectBox.js +128 -0
  91. fnschoo1/static_collected/admin/js/SelectFilter2.js +503 -0
  92. fnschoo1/static_collected/admin/js/actions.js +232 -0
  93. fnschoo1/static_collected/admin/js/admin/DateTimeShortcuts.js +496 -0
  94. fnschoo1/static_collected/admin/js/admin/RelatedObjectLookups.js +276 -0
  95. fnschoo1/static_collected/admin/js/autocomplete.js +33 -0
  96. fnschoo1/static_collected/admin/js/calendar.js +251 -0
  97. fnschoo1/static_collected/admin/js/cancel.js +29 -0
  98. fnschoo1/static_collected/admin/js/change_form.js +21 -0
  99. fnschoo1/static_collected/admin/js/collapse.js +43 -0
  100. fnschoo1/static_collected/admin/js/core.js +174 -0
  101. fnschoo1/static_collected/admin/js/filters.js +37 -0
  102. fnschoo1/static_collected/admin/js/inlines.js +439 -0
  103. fnschoo1/static_collected/admin/js/jquery.init.js +8 -0
  104. fnschoo1/static_collected/admin/js/nav_sidebar.js +81 -0
  105. fnschoo1/static_collected/admin/js/popup_response.js +24 -0
  106. fnschoo1/static_collected/admin/js/prepopulate.js +43 -0
  107. fnschoo1/static_collected/admin/js/prepopulate_init.js +20 -0
  108. fnschoo1/static_collected/admin/js/theme.js +57 -0
  109. fnschoo1/static_collected/admin/js/urlify.js +529 -0
  110. fnschoo1/static_collected/admin/js/vendor/jquery/jquery.js +10913 -0
  111. fnschoo1/static_collected/admin/js/vendor/jquery/jquery.min.js +2 -0
  112. fnschoo1/static_collected/admin/js/vendor/select2/i18n/af.js +43 -0
  113. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ar.js +36 -0
  114. fnschoo1/static_collected/admin/js/vendor/select2/i18n/az.js +33 -0
  115. fnschoo1/static_collected/admin/js/vendor/select2/i18n/bg.js +38 -0
  116. fnschoo1/static_collected/admin/js/vendor/select2/i18n/bn.js +39 -0
  117. fnschoo1/static_collected/admin/js/vendor/select2/i18n/bs.js +48 -0
  118. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ca.js +41 -0
  119. fnschoo1/static_collected/admin/js/vendor/select2/i18n/cs.js +62 -0
  120. fnschoo1/static_collected/admin/js/vendor/select2/i18n/da.js +37 -0
  121. fnschoo1/static_collected/admin/js/vendor/select2/i18n/de.js +41 -0
  122. fnschoo1/static_collected/admin/js/vendor/select2/i18n/dsb.js +51 -0
  123. fnschoo1/static_collected/admin/js/vendor/select2/i18n/el.js +43 -0
  124. fnschoo1/static_collected/admin/js/vendor/select2/i18n/en.js +41 -0
  125. fnschoo1/static_collected/admin/js/vendor/select2/i18n/es.js +41 -0
  126. fnschoo1/static_collected/admin/js/vendor/select2/i18n/et.js +38 -0
  127. fnschoo1/static_collected/admin/js/vendor/select2/i18n/eu.js +45 -0
  128. fnschoo1/static_collected/admin/js/vendor/select2/i18n/fa.js +42 -0
  129. fnschoo1/static_collected/admin/js/vendor/select2/i18n/fi.js +42 -0
  130. fnschoo1/static_collected/admin/js/vendor/select2/i18n/fr.js +43 -0
  131. fnschoo1/static_collected/admin/js/vendor/select2/i18n/gl.js +40 -0
  132. fnschoo1/static_collected/admin/js/vendor/select2/i18n/he.js +44 -0
  133. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hi.js +40 -0
  134. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hr.js +45 -0
  135. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hsb.js +51 -0
  136. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hu.js +44 -0
  137. fnschoo1/static_collected/admin/js/vendor/select2/i18n/hy.js +40 -0
  138. fnschoo1/static_collected/admin/js/vendor/select2/i18n/id.js +36 -0
  139. fnschoo1/static_collected/admin/js/vendor/select2/i18n/is.js +37 -0
  140. fnschoo1/static_collected/admin/js/vendor/select2/i18n/it.js +43 -0
  141. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ja.js +40 -0
  142. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ka.js +42 -0
  143. fnschoo1/static_collected/admin/js/vendor/select2/i18n/km.js +38 -0
  144. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ko.js +42 -0
  145. fnschoo1/static_collected/admin/js/vendor/select2/i18n/lt.js +45 -0
  146. fnschoo1/static_collected/admin/js/vendor/select2/i18n/lv.js +41 -0
  147. fnschoo1/static_collected/admin/js/vendor/select2/i18n/mk.js +42 -0
  148. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ms.js +38 -0
  149. fnschoo1/static_collected/admin/js/vendor/select2/i18n/nb.js +38 -0
  150. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ne.js +44 -0
  151. fnschoo1/static_collected/admin/js/vendor/select2/i18n/nl.js +46 -0
  152. fnschoo1/static_collected/admin/js/vendor/select2/i18n/pl.js +43 -0
  153. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ps.js +41 -0
  154. fnschoo1/static_collected/admin/js/vendor/select2/i18n/pt-BR.js +39 -0
  155. fnschoo1/static_collected/admin/js/vendor/select2/i18n/pt.js +41 -0
  156. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ro.js +43 -0
  157. fnschoo1/static_collected/admin/js/vendor/select2/i18n/ru.js +48 -0
  158. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sk.js +61 -0
  159. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sl.js +41 -0
  160. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sq.js +43 -0
  161. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sr-Cyrl.js +48 -0
  162. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sr.js +48 -0
  163. fnschoo1/static_collected/admin/js/vendor/select2/i18n/sv.js +40 -0
  164. fnschoo1/static_collected/admin/js/vendor/select2/i18n/th.js +36 -0
  165. fnschoo1/static_collected/admin/js/vendor/select2/i18n/tk.js +36 -0
  166. fnschoo1/static_collected/admin/js/vendor/select2/i18n/tr.js +40 -0
  167. fnschoo1/static_collected/admin/js/vendor/select2/i18n/uk.js +59 -0
  168. fnschoo1/static_collected/admin/js/vendor/select2/i18n/vi.js +37 -0
  169. fnschoo1/static_collected/admin/js/vendor/select2/i18n/zh-CN.js +36 -0
  170. fnschoo1/static_collected/admin/js/vendor/select2/i18n/zh-TW.js +33 -0
  171. fnschoo1/static_collected/admin/js/vendor/select2/select2.full.js +7115 -0
  172. fnschoo1/static_collected/admin/js/vendor/select2/select2.full.min.js +2 -0
  173. fnschoo1/static_collected/admin/js/vendor/xregexp/xregexp.js +4993 -0
  174. fnschoo1/static_collected/admin/js/vendor/xregexp/xregexp.min.js +160 -0
  175. fnschoo1/static_collected/css/bootstrap.min.css +11776 -0
  176. fnschoo1/static_collected/css/fnschool.css +26 -0
  177. fnschoo1/static_collected/js/bootstrap.bundle.min.js +4223 -0
  178. fnschoo1/static_collected/js/bootstrap.min.js +2919 -0
  179. fnschoo1/static_collected/js/fnschool.js +84 -0
  180. fnschoo1/static_collected/js/jquery.min.js +5413 -0
  181. fnschoo1/static_collected/js/jquery.slim.min.js +4331 -0
  182. fnschoo1/static_collected/js/popper.min.js +1306 -0
  183. fnschoo1/templates/base/_css.html +1 -0
  184. fnschoo1/templates/base/_js.html +15 -0
  185. fnschoo1/templates/base/content.html +30 -0
  186. fnschoo1/templates/base/header_content_footer.html +35 -0
  187. fnschoo1/templates/close.html +11 -0
  188. fnschoo1/templates/home.html +51 -0
  189. fnschoo1/templates/includes/_footer.html +39 -0
  190. fnschoo1/templates/includes/_header.html +77 -0
  191. fnschoo1/templates/includes/_navigation.html +0 -0
  192. fnschoo1/templates/includes/_paginator.html +27 -0
  193. fnschoo1/templates/registration/logged_out.html +0 -0
  194. fnschoo1/templates/registration/login.html +0 -0
  195. fnschool-20251011.80531.840.dist-info/METADATA +179 -0
  196. fnschool-20251011.80531.840.dist-info/RECORD +207 -0
  197. {fnschool-20250109.80531.837.dist-info → fnschool-20251011.80531.840.dist-info}/WHEEL +1 -1
  198. fnschool-20251011.80531.840.dist-info/entry_points.txt +2 -0
  199. fnschool-20251011.80531.840.dist-info/top_level.txt +1 -0
  200. fnschool/__init__.py +0 -35
  201. fnschool/__main__.py +0 -16
  202. fnschool/app.py +0 -103
  203. fnschool/canteen/__init__.py +0 -3
  204. fnschool/canteen/__main__.py +0 -3
  205. fnschool/canteen/bill.py +0 -253
  206. fnschool/canteen/canteen.py +0 -1
  207. fnschool/canteen/canteen.toml +0 -61
  208. fnschool/canteen/config.py +0 -10
  209. fnschool/canteen/consuming.py +0 -53
  210. fnschool/canteen/currency.py +0 -17
  211. fnschool/canteen/data/bill.i18n.xlsx +0 -0
  212. fnschool/canteen/data/bill.xlsx +0 -0
  213. fnschool/canteen/data/consuming.xlsx +0 -0
  214. fnschool/canteen/data/purchase_list.xlsx +0 -0
  215. fnschool/canteen/entry.py +0 -40
  216. fnschool/canteen/food.py +0 -206
  217. fnschool/canteen/food_classes.py +0 -33
  218. fnschool/canteen/food_classes.toml +0 -64
  219. fnschool/canteen/operator.py +0 -91
  220. fnschool/canteen/path.py +0 -28
  221. fnschool/canteen/spreadsheet/base.py +0 -213
  222. fnschool/canteen/spreadsheet/consuming.py +0 -310
  223. fnschool/canteen/spreadsheet/consumingsum.py +0 -76
  224. fnschool/canteen/spreadsheet/cover.py +0 -64
  225. fnschool/canteen/spreadsheet/ctspreadsheet.py +0 -351
  226. fnschool/canteen/spreadsheet/food.py +0 -680
  227. fnschool/canteen/spreadsheet/inventory.py +0 -375
  228. fnschool/canteen/spreadsheet/merging.py +0 -340
  229. fnschool/canteen/spreadsheet/preconsuming.py +0 -329
  230. fnschool/canteen/spreadsheet/purchasing.py +0 -885
  231. fnschool/canteen/spreadsheet/purchasingsum.py +0 -110
  232. fnschool/canteen/spreadsheet/spreadsheet.py +0 -363
  233. fnschool/canteen/spreadsheet/translating.py +0 -12
  234. fnschool/canteen/spreadsheet/unwarehousing.py +0 -178
  235. fnschool/canteen/spreadsheet/unwarehousingsum.py +0 -11
  236. fnschool/canteen/spreadsheet/warehousing.py +0 -360
  237. fnschool/canteen/spreadsheet/workbook.py +0 -17
  238. fnschool/canteen/test.py +0 -97
  239. fnschool/config.py +0 -48
  240. fnschool/entry.py +0 -67
  241. fnschool/exam/__init__.py +0 -8
  242. fnschool/exam/data/parental_emails.xlsx +0 -0
  243. fnschool/exam/data/score.xlsx +0 -0
  244. fnschool/exam/email.py +0 -349
  245. fnschool/exam/entry.py +0 -36
  246. fnschool/exam/language.py +0 -19
  247. fnschool/exam/path.py +0 -24
  248. fnschool/exam/score.py +0 -1191
  249. fnschool/exam/subject.py +0 -20
  250. fnschool/exam/teacher.py +0 -54
  251. fnschool/external.py +0 -89
  252. fnschool/games/__init__.py +0 -1
  253. fnschool/games/__main__.py +0 -1
  254. fnschool/games/games.py +0 -1
  255. fnschool/inoutput.py +0 -97
  256. fnschool/language.py +0 -40
  257. fnschool/locales/en_US/LC_MESSAGES/fnschool.mo +0 -0
  258. fnschool/locales/zh_CN/LC_MESSAGES/fnschool.mo +0 -0
  259. fnschool/locales/zh_HK/LC_MESSAGES/fnschool.mo +0 -0
  260. fnschool/locales/zh_SG/LC_MESSAGES/fnschool.mo +0 -0
  261. fnschool/locales/zh_TW/LC_MESSAGES/fnschool.mo +0 -0
  262. fnschool/path.py +0 -45
  263. fnschool/test.py +0 -24
  264. fnschool/user.py +0 -262
  265. fnschool-20250109.80531.837.dist-info/METADATA +0 -342
  266. fnschool-20250109.80531.837.dist-info/RECORD +0 -78
  267. fnschool-20250109.80531.837.dist-info/entry_points.txt +0 -5
  268. fnschool-20250109.80531.837.dist-info/top_level.txt +0 -1
  269. /fnschool/canteen/consume.py → /fnschoo1/canteen/__init__.py +0 -0
  270. /fnschool/canteen/food_recount.toml → /fnschoo1/canteen/migrations/__init__.py +0 -0
  271. {fnschool/canteen/spreadsheet → fnschoo1/canteen/workbook}/__init__.py +0 -0
  272. /fnschool/exam/__main__.py → /fnschoo1/fnschool/templatetags/__init__.py +0 -0
  273. /fnschool/canteen/food_recounts.toml → /fnschoo1/profiles/__init__.py +0 -0
  274. /fnschool/canteen/warehouse.py → /fnschoo1/profiles/migrations/__init__.py +0 -0
  275. /fnschool/canteen/workbook.toml → /fnschoo1/templates/base/_content.html +0 -0
  276. {fnschool-20250109.80531.837.dist-info → fnschool-20251011.80531.840.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 %}