Compare commits

...
499 Commits
Author SHA1 Message Date
tanshu 2dfbfd8fcb Version Bump v15.1.6 2026-09-04 15:29:38 +05:30
tanshu 221fadfdfc Fix: Select customer could not edit or create a customer. 2026-09-04 09:59:29 +00:00
tanshu 11b93cb11c Version Bump v15.1.5 2026-09-04 15:17:50 +05:30
tanshu 302a4eb311 Fix: Receive payment refactored. Earlier it would refresh on input. 2026-09-04 09:47:42 +00:00
tanshu 217f636573 Version Bump v15.1.4 2026-09-04 13:44:05 +05:30
tanshu 95ac73bbc4 Updated tracking in template and choose customer fixing. 2026-09-04 08:13:26 +00:00
tanshu 3d86f8706f Version Bump v15.1.3 2026-09-04 09:03:38 +05:30
tanshu 30cbb60efe Choose customer would refresh while fetching customers. 2026-09-04 03:33:17 +00:00
tanshu 227f5b5f74 Version Bump v15.1.2 2026-09-02 18:38:08 +05:30
tanshu 50974c7574 Added a role for printer so that the container can depend on it. 2026-09-02 13:07:50 +00:00
tanshu c8e1ab74b6 Version Bump v15.1.1 2026-09-02 18:36:55 +05:30
tanshu 85ba7b326a Fix: Edge cases where backend and frontend totals are off and you cannot settle the bill. 2026-09-02 13:06:34 +00:00
tanshu 458dfaad50 Version Bump v15.1.0 2026-09-02 09:10:23 +05:30
tanshu 0a9675f270 We have moved from dd-MMM-yyyy to ISO Date. 2026-09-02 03:38:49 +00:00
tanshu 6cf42d3641 Added a handler to send the client errors to the backend. Updated the launch settings to reload the fastapi app on file changes. 2026-09-02 02:35:53 +00:00
tanshu cf680cd254 Version Bump v15.0.4 2026-09-01 07:45:23 +05:30
tanshu 80d21ec31e Minor fixes. Logs should now show the real ip address not the caddy container ip. 2026-09-01 02:15:01 +00:00
tanshu 4377016cda Version Bump v15.0.3 2026-08-31 12:11:38 +05:30
tanshu 67209886b9 current trixie does not have yarn 2026-08-31 06:41:31 +00:00
tanshu 4e1c0d1ec7 Version Bump v15.0.2 2026-08-31 10:02:01 +05:30
tanshu c7412f080d Fix: Newer trixie images did not enable yarn. 2026-08-31 04:31:48 +00:00
tanshu d023089720 Version Bump v15.0.1 2026-08-31 09:55:05 +05:30
tanshu dd02a97ba2 Forgot to add the updated uv.lock 2026-08-31 04:25:00 +00:00
tanshu befd4eb298 Version Bump v15.0.0 2026-08-31 04:23:12 +00:00
tanshu d90c1cb563 Moved from moment to luxon.
Report Printing is not a post not a get.
2026-08-31 04:19:05 +00:00
tanshu e90c80999e Frank now working with netbird and nats + jetstream 2026-08-30 15:57:59 +00:00
tanshu 42331b482d ansible improvements 2026-08-30 14:56:38 +00:00
tanshu 612e2243c0 Working on frand deployement 2026-08-30 10:37:09 +00:00
tanshu 35a3bb9ad2 netbird server works now. linted the yml files 2026-08-30 07:35:08 +00:00
tanshu ae69c2fafe Frank refactoring part 1 2026-08-30 04:41:27 +00:00
tanshu 85a063c1a7 fdsa 2026-08-29 21:09:42 +00:00
tanshu 10448e8b12 fds 2026-08-29 12:19:14 +00:00
tanshu 59bf2c5ca9 dfsa 2026-08-29 10:52:44 +00:00
tanshu 73fba17711 fdsa 2026-08-29 10:49:36 +00:00
tanshu 103983846d fasd 2026-08-28 15:44:19 +00:00
tanshu af07bb5043 fdsa 2026-08-27 12:08:58 +00:00
tanshu 612426b92f fasd 2026-08-27 11:56:52 +00:00
tanshu 730048f239 fda 2026-08-27 11:52:55 +00:00
tanshu ff537edb06 fds 2026-08-27 11:38:38 +00:00
tanshu 311464e593 csdxz 2026-08-27 06:06:30 +00:00
tanshu 43204e8441 fad 2026-08-27 05:38:32 +00:00
tanshu 2a29dc9195 cdsa 2026-08-18 13:13:11 +00:00
tanshu 65527dcbff gdfasg 2026-08-18 12:47:58 +00:00
tanshu cc52670783 mid 2026-08-18 12:28:51 +00:00
tanshu 993f83c786 toSignal via Gemini 2026-08-18 07:55:54 +00:00
tanshu 59eb45da96 Version Bump v14.5.0 2026-08-10 14:04:26 +05:30
tanshu ebe5a6352e Made the version bump more robust. 2026-08-10 08:33:15 +00:00
tanshu 38a8656008 Added proper agents config for mcp servers and skills.
Added the menu category delete route.
Fix: Show blank vouchers in tables
Fix: In rare cases, the old settements can stick around. fixed it.
2026-08-10 08:25:12 +00:00
tanshu 2e5543a2eb Optimizing and updating Dockerfile for build testing, size and cache. 2026-02-22 05:07:43 +00:00
tanshu 34e30efcaf Chore: Ruff linting updated and fixed errors.
Chore: Moved from poetry to uv
2026-02-22 04:23:14 +00:00
tanshu 2b844bc44b Chore: Using FastAPI Exception handlers for Integrity Errors and SQLAlchemyErrors.
Chore: Using Annotation to inject Session in routes.
Hence, have removed all with Session and try / catch
2026-02-21 08:26:29 +00:00
tanshu 325bcae998 Version Bump v14.3.1 2026-02-20 21:58:40 +05:30
tanshu ab8fed17d0 Fix: Product update failed as sku_version_id was not sent on the schema and hence update tried to create a new sku.
Fix: If a product sku was updated and then deleted on the same day, it failed as the valid_till for the sku_version was set to yesterday while the valid_from was set to today.
Fix: Product update also failed as the db.commit was on the loop adding new skus
2026-02-20 16:27:14 +00:00
tanshu dcf1ffad98 Version Bump v14.3.0 2026-02-17 09:45:32 +05:30
tanshu 338d9d63d5 Validation: Making Product name unique ignoring the fraction_units.
Making the sku_version.units unique for a product across stock_keeping_units.

Both respecting the validity
2026-02-17 04:13:11 +00:00
tanshu 9b0da9cb65 Fix: Product sale report was aggregating on the product_version_id 2026-02-14 12:20:09 +00:00
tanshu 4cc2ff2229 Version Bump v14.2.1 2026-02-14 11:30:40 +05:30
tanshu c9fa83e8ac Fix: Accidentally used menu category instead of sale category 2026-02-14 06:00:09 +00:00
tanshu 061dd1310d Version Bump v14.2.0 2026-02-14 05:30:05 +00:00
tanshu a1d6919d9d Beer consumption report can choose menu category for beer 2026-02-14 05:29:11 +00:00
tanshu ecc92795e6 Fix: In product list, if menu category is reset, all products get filtered out as it becomes undefined. 2026-02-12 09:01:12 +00:00
tanshu 535a145742 Fix: Cannot move kot as row.kotId was always undefined. 2026-02-12 08:20:54 +00:00
tanshu 1271e26dd8 Changed Docker CMD to remove redundant run.sh 2026-02-12 04:01:35 +00:00
tanshu 2ae3f3ba7e Dockerfile will kill the process if stuck so that restart policy can restart the container
Gunicorn will kill worker every 10000 requests which can help reclaim memory lost to leaks
2026-02-12 03:52:53 +00:00
tanshu e6a03d33e5 Version Bump v14.1.0 2026-02-11 14:59:50 +05:30
tanshu c7dd6e574d Feature: Roles can include Roles. 2026-02-11 09:28:50 +00:00
tanshu 913820cc29 Dockerfile improvements. 2026-02-11 08:41:12 +00:00
tanshu e3d40d50f0 Fix: Unable to receive payment as the amountVal as always zero. 2026-02-11 08:37:12 +00:00
tanshu b27a5a9211 Committed the poetry.lock 2026-02-11 05:51:10 +00:00
tanshu a955e26b93 Added poetry lockfile to Dockerfile 2026-02-11 05:48:41 +00:00
tanshu bd2b1f5c2f Version Bump v14.0.1 2026-02-11 10:53:26 +05:30
tanshu 0fa28019ec Fix: Accidentally removed the static files route needed to serve the frontend in deployment 2026-02-11 05:23:08 +00:00
tanshu ddb03ac8eb Version Bump v14.0.0 2026-02-11 10:43:43 +05:30
tanshu ae8975b29f Merge branch 'feature/bundle' 2026-02-11 10:42:01 +05:30
tanshu e7e49019c4 Schemas everywhere mypy and ruff fully compliant
Type hints everywhere
2026-02-11 04:41:22 +00:00
tanshu dc34a78e06 Cleanup 2026-02-10 18:12:52 +00:00
tanshu 91b3740ec0 Bundle item has an option to not print in the bill.
Bills and Kots should print properly with bundles
2026-02-10 18:00:03 +00:00
tanshu 7382d487ac Product Sale Report double counts the Bundle Items as it lists them as well as the bundle.
Menu Engineering Report double counts the Bundle Items as it lists them as well as the bundle.

All reports fixed.
2026-02-05 01:12:56 +00:00
tanshu 8957e4ce8c All voucher routes now work with bundles.
Need to check / update all reports and prints
2026-02-05 00:46:49 +00:00
tanshu 6e8843d3c4 Feature: Added product bundles. Have to wire it up to inventory and reports. 2026-01-31 15:16:58 +00:00
tanshu b79af6e1c6 Changed the for-product route to for-sku to reduce ambiguity 2026-01-29 16:23:07 +00:00
tanshu c65517b377 Fix 2026-01-28 07:18:10 +00:00
tanshu ca94f8f57e Upgraded to Angular v21
Removed tailwind

Moved to vitest from karma/jasmine
2026-01-28 07:18:05 +00:00
tanshu e0766c5a2e Merge branch 'sku' 2026-01-28 04:16:04 +00:00
tanshu bd1a5e03c5 Merge branch 'main' into sku 2026-01-28 09:39:59 +05:30
tanshu 61f5f2f1af Product update sort order working
Beer Sale report should work. But it has all the products in it now.
All reports working
2026-01-28 04:07:22 +00:00
tanshu 0a7ffb4a5c Product list and detail fully working. only thing to check is the update sort order route 2026-01-26 14:01:18 +00:00
tanshu 22f888500f Update product prices now working 2026-01-26 14:00:14 +00:00
tanshu 2505ef7aec Temporal Product Fixed. 2026-01-26 07:07:33 +00:00
tanshu 4f0346551a Modifier Category Working 2026-01-24 03:26:03 +00:00
tanshu e4072d0872 Sort order is no longer versioned. Also, data moved is not normalized to add the right skus to the products and coalesce contiguous products and skus with no changes. 2026-01-23 16:20:08 +00:00
tanshu 1b8ab2857f Mostly working with Product + Version and Sku + Version.
There will still be many errors. But this is working somewhat
2026-01-23 02:30:57 +00:00
tanshu aaf64ab75a Docker health check 2026-01-15 09:25:38 +05:30
tanshu 6caeb34e8d Change: The cashier report will now split the summary and details. 2025-09-09 07:14:48 +05:30
tanshu 88b53b5781 Change: The cashier report will now split the summary and details. 2025-09-09 01:40:07 +00:00
tanshu 8ff7363fc8 Fixed: VoucherType as not number. So, if a bill was being printed directly without kot first being printed, it would not ask for bill type as the comparison was comparing string with int. 2025-08-09 17:19:30 +00:00
tanshu e1cbf7ffb8 Trackby for the bill table. This should prevent rerendering and it jumping up. 2025-08-04 15:27:21 +00:00
tanshu b33468a43f Fix: Show voucher without kots and iventories would cause an error in show. 2025-08-04 14:54:42 +00:00
tanshu 3e8f2e9736 Changed the font 2025-07-29 09:04:03 +00:00
tanshu f2d5d62aa6 Fix: Beer Sale Report 2025-07-17 17:13:35 +00:00
tanshu eee9ae1f11 CSS Class updation 2025-07-17 03:29:14 +00:00
tanshu 2fbfe96c40 Fix: Due to matching of timestamp vs date, on the day that a product valid_till expired,
comparison with voucher.date would wrongly fail.
2025-07-17 03:28:22 +00:00
tanshu 01b0cbfadf Removed Unnecessary Flex class 2025-07-11 11:44:28 +00:00
tanshu 88a1572747 Chore: Removed Mat Card from everywhere.
Chore: Removed the Toaster Service.
Chore: Updated to Material Theme 3
2025-07-11 11:30:35 +00:00
tanshu 5c1c474e93 Fix: Product Sale Report was not showing "H H " and also printing was an issue. 2025-07-10 16:05:56 +00:00
tanshu 74e17dffd0 Version Bump v13.1.0 2025-07-10 10:59:03 +00:00
tanshu 8ad9e6562c Removed all mr-5, mb-5 and basis tailwind classes.
Will soon remove tailwind totally.
2025-07-10 10:58:48 +00:00
tanshu d80d097c0e Fix: Sale category was not opening as product query error of sorting on menu category without a join 2025-07-10 10:56:30 +00:00
tanshu 0dbec4784b Feature: Paged customer list. 2025-07-10 09:39:55 +00:00
tanshu af684c976e Version Bump v13.0.1 2025-07-09 15:37:37 +00:00
tanshu 26c65b4b65 Fix: Could not open bill with bill number or voucher id 2025-07-09 15:35:35 +00:00
tanshu 2c89e218c8 Version Bump v13.0.0 2025-07-09 14:14:01 +00:00
tanshu b2732434f4 Fix: Kots were not sorted in show voucher. 2025-07-09 14:10:31 +00:00
tanshu 5317e3de23 Fix: Vouchertype as int not string. This fucked up everthing in the frontend and was not able to print kot or bills. 2025-07-09 06:37:31 +00:00
tanshu add3ad35d7 Fix: Missing report schemas 2025-07-09 05:05:30 +00:00
tanshu feb867fe28 Fix: Ansible 2025-07-09 05:04:19 +00:00
tanshu fc2ef36a0c Update for new deployment with Caddy and docker networks 2025-07-09 03:46:33 +00:00
tanshu 31b4ee1ebf Chore: fixed a lot of mypy errors. 2025-07-03 06:27:10 +00:00
tanshu 0e9d9c5934 In the following using onclause for joining ProductVersion vs where:
Product Router
Beer Sale Report
Discount Report
Menu Engineering Report
Product Sale Report
Product Updates Report
Sale Report


Create a pydantic schema for show voucher

Fixed modifier dialog in sales to original size.
2025-07-02 17:44:39 +00:00
tanshu 5401318fc1 Serving method changed to caddy 2025-07-02 04:48:06 +00:00
tanshu 66834b7399 Fix: Modifier Category was showing products from out of category. 2025-07-02 04:34:13 +00:00
tanshu 44513dd6be Moved to Angular 20
Moved to Tailwind 4
Moved to Python 3.13
Enabled arm64/v8 Builds
2025-07-02 04:32:35 +00:00
tanshu 86722e3558 Version Bump v12.1.8 2025-05-27 13:58:48 +00:00
tanshu 2ca94e5bea Add tax was not working 2025-05-27 13:57:46 +00:00
tanshu ad8ccb26df Version Bump v12.1.7 2025-05-02 02:33:32 +00:00
tanshu abc1b36721 Auth errors were not showing up as they were swallowed up by the auth interceptor.
Once caught, the device name was not being shown as it was triggering an expression changed after detection error.
2025-05-02 02:02:25 +00:00
tanshu 68ab90ec48 Chore: Cleaned up imports to use Modules.
Feature: Added an eslint rule to sort component imports
2024-12-19 08:29:05 +05:30
tanshu 7be7545137 Version Bump v12.1.6 2024-12-17 22:26:50 +05:30
tanshu b46fd10f5a Modifier was not working as when the data came late, it would display blank. 2024-12-17 22:26:35 +05:30
tanshu 50846ace3f Version Bump v12.1.5 2024-12-17 21:21:31 +05:30
tanshu e36de39ed5 Modifiers issues 2024-12-17 21:21:21 +05:30
tanshu 4b1fca0260 Version Bump v12.1.4 2024-12-17 09:47:34 +05:30
tanshu 43a884144b Fix: Issues flagged by mypy 2024-12-17 09:47:25 +05:30
tanshu 85a939b761 Fix: Show section wise settlement and tax 2024-12-17 09:46:44 +05:30
tanshu 3bfa00364c Version Bump v12.1.3 2024-12-17 09:18:43 +05:30
tanshu 0b092b5467 Fix: login failed as python-jose was removed. 2024-12-17 09:18:33 +05:30
tanshu 82747c4b89 Version Bump v12.1.2 2024-12-17 09:04:56 +05:30
tanshu e732354671 Fix: run was referencing files in a subdirectory.
Fix: Playbook was not importing the default vars.
Improvement: Dockerfile casheing should improve.
2024-12-17 09:04:49 +05:30
tanshu 6d3057bb55 Version Bump v12.1.1 2024-12-17 08:58:54 +05:30
tanshu 15a93c599e Chore: ruff check will fix the errors also. 2024-12-17 08:58:45 +05:30
tanshu 823e3a761e Fix: Docker entrypoint command. run.sh is in the /app subdirectory 2024-12-17 08:58:17 +05:30
tanshu ac728128b9 Chore: Cleanup Module imports 2024-12-17 08:57:52 +05:30
tanshu 5536bcae41 Version Bump v12.1.0 2024-12-17 08:47:13 +05:30
tanshu 55d5017254 Product sale report can now be section wise 2024-12-17 08:46:29 +05:30
tanshu bbb7be8070 Sale report now can be section wise 2024-12-17 08:23:18 +05:30
tanshu b1407f339d Version Bump v12.0.1 2024-12-16 21:18:26 +05:30
tanshu 659fed549a Fix: Discount and payment received were not working 2024-12-16 21:17:58 +05:30
tanshu a5efcc9061 Fix: Dockerfile was erroring out. 2024-12-16 19:03:38 +05:30
tanshu 7e70214c94 Version Bump v12.0.0 2024-12-16 17:58:20 +05:30
tanshu 2495c24e1a Chore: Updated python dependencies
Chore: Updated angular to v19
Chore: Refactored ops with docker and ansible
2024-12-16 17:58:17 +05:30
tanshu 010e9a84db Chore: Upgrade to Angular v18
Chore: Upgrade to Python 3.12
Chore: Upgrade to psycopg3
2024-06-03 13:22:56 +05:30
tanshu 56c1be5e05 Chore: Moved from isort + flake8 to ruff. There are a few formatting differences in isort and ruff
Chore: Updated dependencies
2023-08-31 07:16:58 +05:30
tanshu cbd5da12cc Version Bump v11.7.0 2023-08-28 11:03:22 +05:30
tanshu 93b1b8840d Feature: Updated the frank dockerfile to properly install python poetry
Feature: Updated the frank playbook to allow user to be changed from pi
Chore: Refactored the various env files to use single file and multiple var files.
2023-08-28 11:03:00 +05:30
tanshu ecea277e46 Feature: Table Booking in guestbook.
Feature: Guest book list shows the running cover count
2023-08-28 07:14:24 +05:30
tanshu 1cb2677ad1 Chore: Moved edit and delete in guestbook to the details from list.
This will give more space.
Also removed the old table button
2023-08-17 22:18:14 +05:30
tanshu e8a33cd80e Chore: Updated bake pies to current services 2023-08-17 22:17:09 +05:30
tanshu cb3657615a Chore: Updated fastapi and pydantic
Refactor: Removed mapped_column name string in models
Refactor: Moved from postgres.uuid to uuid in models
Fix: Schemas use Daf (Decimal as float to hack around pydantic v2 sending decimals as strings.)
2023-08-17 22:09:20 +05:30
tanshu e955ef4196 Version Bump v11.6.3 2023-08-07 17:45:35 +05:30
tanshu f1ef578eae Fix: Schema would show the full price for happy hour products 2023-08-07 17:43:09 +05:30
tanshu 05302c4291 Version Bump v11.6.2 2023-07-26 13:00:19 +05:30
tanshu 96e54e2a0a Fix: Repint was creating new bills. Rounding caused it to think that the amounts had changed.
Made the inventory amount expression round to even and the inventory schema to validate to round the inputs
2023-07-26 13:00:01 +05:30
tanshu 26310dfc42 Changed docker to use a hosts file with various var files and to use roles. 2023-07-23 08:46:45 +05:30
tanshu 16f29c4139 Version Bump v11.6.1 2023-07-22 14:57:57 +05:30
tanshu f8c1e0591b Fix: Discount not showing and other chores 2023-07-22 14:57:50 +05:30
tanshu 42102d7fef Version Bump v11.6.0 2023-07-22 09:22:14 +05:30
tanshu 09b762d154 Chore:
Moved to Angular 16
Moved to FastAPI 0.100.0
Moved to pydantic 2.0
2023-07-22 09:20:01 +05:30
tanshu 8b2d15b9e1 Fix: Guestbook would not let you seat anyone who has any old bill.
Fix: Only create a new temporal product when there is some change in the data.
2023-07-20 07:00:20 +05:30
tanshu 5534b3983d Moved back from devcontainer to normal as ubuntu now has python 3.11 2023-07-20 06:59:32 +05:30
tanshu e685941804 Version Bump v11.5.0 2023-04-09 15:43:00 +05:30
tanshu 8bc7d66123 Fix: Prevent creation of customer with blank name when adding a guest book item.
Fix: Also prevent creation of customer with blank phone number when adding a guest book.
Feature: Show the old bill of a customer in guest book
Fix: In reprint, allow changing of customer
Chore: Updated dependencies
2023-04-09 15:42:32 +05:30
tanshu 302ed4a18f Version Bump v11.4.2 2023-03-24 12:46:54 +05:30
tanshu 2fb2e01ca1 Feature: Allow edit of time in guest book.
Feature: Guest book row color is the same as running table colors
2023-03-24 12:46:42 +05:30
tanshu 94cc8ccd47 Version Bump v11.4.1 2023-03-24 10:17:16 +05:30
tanshu c13047e812 Fix: Check if moved/merged kots could lead to negative products of a situation where happy hour products were more than regular products.
Case:
  Kot 1:
    Beer Mug => 2 regular, 2 happy hour
  Kot 2:
    Beer Mug => 2 Regular
  Kot 3:
    Beer Mug => -2 Regular

Then move Kot 2 to new table would leave the voucher with 2 hh and 0 regular
Also, just move did not check if products were going negative.
2023-03-24 10:17:06 +05:30
tanshu 5c697dd43a Version Bump v11.4.0 2023-03-24 09:10:20 +05:30
tanshu 56ae7500cc Feature: Allow bills without items so that tables can be seated from the guest book.
Feature: Allow guest book entries to be associated with running vouchers
Feature: Allow removing customer from voucher
2023-03-24 09:09:13 +05:30
tanshu 1b4c26733d Chore: Refactor nil inventories and kots checking 2023-03-24 08:49:19 +05:30
tanshu 88bda14002 Fix: Typo 2023-03-24 08:48:33 +05:30
tanshu 7a8017087b Fix: Update pax if changed
Fix: Tax was associated with inventory, not just tax_id so that if along with adding an inventory, the bill is printed then it can be used to get regime information.
Chore: Refactored checking for nil inventory and kots.
2023-03-24 08:48:18 +05:30
tanshu 1297a4e7d2 Cancel VoucherType was added 2023-03-24 08:45:52 +05:30
tanshu 47afb7e29c Always ensure that a "KOT" Bill Number was generated even if the bill was straight printed. 2023-03-24 08:44:48 +05:30
tanshu 4db26722a3 Fix: Debugging was left in the report 2023-03-24 08:43:54 +05:30
tanshu b2db7e8f9c Version Bump v11.3.3 2023-03-19 23:08:22 +05:30
tanshu f6deea265c Fix: Error is checking length of bills 2023-03-19 23:08:14 +05:30
tanshu f1d145e76f Version Bump v11.3.2 2023-03-19 23:05:00 +05:30
tanshu 22f61427b0 Fix: Cashier report was only showing the last amount of aggregate items.
When it was not added to info, the amount also got reset to 0

Feature: Disabled total slip for single bills
2023-03-19 22:44:38 +05:30
tanshu f9fc6dba0d Version Bump v11.3.1 2023-03-19 14:43:45 +05:30
tanshu e56a753053 Fix: Users without ability of edit printed products where not able to set quantity to zero in the bill 2023-03-19 14:43:40 +05:30
tanshu 03918fee80 Version Bump v11.3.0 2023-03-19 09:56:58 +05:30
tanshu c14e64ce85 Feature: The menu engineering report now recalculates the percentages based on filter 2023-03-19 09:56:48 +05:30
tanshu 4e4cebf5d4 Chore: Moved the formatting to the main style sheet because it was used everywhere.
Also, moved to sass to get theme colors from the material design library
2023-03-18 23:29:10 +05:30
tanshu d39712a347 Feature: Made a Menu Engineering Report 2023-03-18 23:20:11 +05:30
tanshu 40a357edc8 Version Bump v11.2.0 2023-03-17 10:18:40 +05:30
tanshu 1a08066c2d Feature: Cancelled bills are now separate from void bills.
Void bills are automatically generated when printed bills are changed.
Manually cancelled bills are called cancelled bills.
2023-03-17 08:27:30 +05:30
tanshu a62018ad1f Version Bump v11.1.2 2023-03-14 13:27:40 +05:30
tanshu da46591313 Fix: Happy hour product button color was being overwritten by the face color 2023-03-14 13:27:05 +05:30
tanshu a1ccf973cd Version Bump v11.1.1 2023-03-14 00:15:50 +05:30
tanshu 65700d2760 Fix: Date was not updated when the bill was printed. 2023-03-14 00:15:35 +05:30
tanshu 77c7b167ef Version Bump v11.1.0 2023-03-13 23:52:56 +05:30
tanshu efa2af396d Chore: Moved from css to sass, god knows what to do now.
Chore: Prettier line length changed to 120 from 100
Fix: Hard coded the face as the primary color to make the buttons stand out
2023-03-13 23:52:44 +05:30
tanshu b021861ba3 Version Bump v11.0.2 2023-03-13 21:22:24 +05:30
tanshu 245937ecf2 Fix: The dialogs were not rendering properly.
The select table dialog was long and narrow.
The billtype was selection was also not showing properly.
2023-03-13 21:22:07 +05:30
tanshu 3af0b0ac96 Version Bump v11.0.1 2023-03-13 20:40:10 +05:30
tanshu 4fffdd85d6 Fix: Merge table did not delete the old bills and fucked up.
Fix: Bill settlement report was not in order
2023-03-13 20:38:55 +05:30
tanshu 7da9212cf7 Version Bump v11.0.0 2023-03-13 11:11:42 +05:30
tanshu 0f1660ef5a Fix: Regime was stripping the whitespaces from header 2023-03-06 22:02:21 +05:30
tanshu 7d0874093c Fix: New Product did not work due to missing quantity is the schema
Fix: New Product would not auto add modifiers
2023-03-06 21:42:09 +05:30
tanshu 516b22ed1c Refactored customer discount and choose discount. They were using the same schema unnecessarily which was leading to confusion. 2023-03-06 21:04:44 +05:30
tanshu c5eaeb1497 Fix: Receive payment did not show display reason 2023-03-06 10:35:17 +05:30
tanshu f408e84af3 Added traceback to save voucher to better understand why an error occurs. 2023-03-06 10:21:27 +05:30
tanshu e46fe7f90e Feature: Tax Regimes are added so that different bills with different series can be printed for Different regimes such as VAT and GST
Chore: Model relationships updated to make them simpler
Chore: Bill printing majorly refactored for it

Due to the sheer depth of the changes. There can be showstoppers. Please test it carefully
2023-03-05 23:50:41 +05:30
tanshu 802eded568 Due to update to sqlalchemy, fixed schema nullability 2023-02-26 19:51:29 +05:30
tanshu 5c7985e392 Chore:
Moved to sqlalchemy 2.0
Added type checking as much as possible
Updated angular to 15
Moved from Angular flex layout to tailwind css

Started developing on vscode with devcontainers
2023-02-20 18:49:17 +05:30
tanshu 792ccf923f Chore:
Upgraded to Angular 14
2022-07-06 09:04:10 +05:30
tanshu f637f01954 Version Bump v10.3.3 2022-06-21 12:40:35 +05:30
tanshu 236c746e1c Fix: Suddenly got errors saying that the redis.enqueue job was running in a different loop 2022-06-21 12:39:51 +05:30
tanshu a4db3b24f0 Version Bump v10.3.2 2022-06-21 08:54:51 +05:30
tanshu 9f4ec3697c Fix: Frank service changed to beacon from knox 2022-06-21 08:54:45 +05:30
tanshu 9fe84800a7 Chore: Dockerfile also updated 2022-06-21 08:53:37 +05:30
tanshu f73de53d21 Version Bump v10.3.1 2022-06-21 08:44:58 +05:30
tanshu e37d160b7c Fix: Square brackets made the expression a list and the next generator would not work on it 2022-06-21 08:44:00 +05:30
tanshu 61bb62475b Chore: Updated dependencies and moved to Python 3.10 method of type hinting 2022-06-21 08:41:57 +05:30
tanshu 2f1f9eee1f Version Bump v10.3.0 2022-04-26 09:07:07 +05:30
tanshu f9124abb6c Moved ssh and redis tunneling from knox to beacon 2022-04-26 09:03:10 +05:30
tanshu 09a8fdfecc Chore: some mypy fixes 2022-04-26 08:59:42 +05:30
tanshu 7a2ccb7fb1 Fix: Sale Report won't add sales of printed but yet unsettled bills to Category wise sale. 2022-04-26 08:47:55 +05:30
tanshu 1eccd7f9d5 Version Bump v10.2.2 2022-04-16 17:29:49 +05:30
tanshu a213c1a0b5 Added: Process id to log.
Added: Max workers to all .envs
Added: Leardall service
Added: beacon redis tunnel
2022-04-16 17:28:00 +05:30
tanshu 1a8992c296 Version Bump v10.2.1 2022-04-16 08:44:24 +05:30
tanshu 676719917a Fix: Wrong parameter to gunicorn run. This as anyway a bit of trial and error 2022-04-16 08:44:17 +05:30
tanshu 1071b57484 Version Bump v10.2.0 2022-04-16 07:59:48 +05:30
tanshu 3eaf4cd41d Using gunicorn to run upto 4 workers. This should fix the problems of the whole program crashing. 2022-04-16 07:57:18 +05:30
tanshu c494284517 Linting broke for v10+ as the regex only matched one digit 2022-04-03 07:32:35 +05:30
tanshu 34ced063a1 Version Bump v10.1.1 2022-04-03 07:31:38 +05:30
tanshu 59f47e046b Version Bump v10.1.0 2022-04-03 07:31:27 +05:30
tanshu 54c797d1a8 Set strict mode on the scripts 2022-04-03 07:27:12 +05:30
tanshu d43aab0b23 Chore: Updated linter changed this. 2022-04-03 07:26:40 +05:30
tanshu 1f1a9a13cd Chore: Update dependencies
Version Bump v10.1.0
2022-04-02 10:00:31 +05:30
tanshu d9d5754e26 Removed async as I suspect it was crashing the entire application. 2022-04-02 09:59:26 +05:30
tanshu a174f6b847 Fix: Product sale report would mix different product versions as it was checking the product id and not version id
Feature: Cannot add na product to bill
2021-12-29 07:49:23 +05:30
tanshu 1a4e51051c Version Bump v10.0.0 2021-10-29 08:06:35 +05:30
tanshu ee7a24fa77 Minor cleanup 2021-10-29 08:06:23 +05:30
tanshu ef9e28c95b Docker: Updated the dockerfile and deploy script to better use docker cacheing 2021-10-28 09:55:09 +05:30
tanshu 0837aebfb8 Chore: Updated dependencies 2021-10-27 10:30:57 +05:30
tanshu 124cf4d9ff Fix: Username unique index was case sensitive and this allowed duplicate names.
Feature: Moved temporal products into their own module and reverted the products module
2021-10-27 09:27:47 +05:30
tanshu debe0df7b7 Chore: Fix import of Observable to be from rxjs and not rxjs/internal. It was maybe causing optimization bailouts 2021-09-20 09:30:00 +05:30
tanshu 5ff954deb6 Fix: Customers with no discount wouldn't show properly in sale / select customer 2021-09-20 09:16:54 +05:30
tanshu b70a296d8b Version Bump v9.8.0 2021-09-19 22:42:38 +05:30
tanshu 6379e5f4e3 Choose / Edit / Create customers during billing. 2021-09-19 22:42:27 +05:30
tanshu c478290da0 Added the public keys of knox and buttercup as well
Disabled password logins to harden the system
2021-09-13 10:03:07 +05:30
tanshu 5bea488181 Allow the renaming of the main section by temporal product permission.
Allow deleting sections which do not have tables, but have associated section printers.
2021-09-04 13:14:27 +05:30
tanshu 63dfe05044 Chore: Changed the Section Printer from Menu Categories to Sale Categories. This is more logical, in the older software, there as no concept of sale categories so menu categories was used.
This should make the whole thing much easier to update and read.
2021-09-04 12:57:56 +05:30
tanshu 4a12ee0834 Allow editing of product version with the right permission. 2021-09-04 12:52:05 +05:30
tanshu f929a731cb Feature: In the Beer Sale Report, you can choose the type of consumption
Chore: Sorted the product sale report by name
Fix: Voided bills now also include the old bill number
2021-08-17 07:37:17 +05:30
tanshu 1515c33e7b arq is right now not compatible with aioredis 2.0.0 So pinning the aioredis to 1.3.1 for the time being 2021-08-10 11:38:44 +05:30
tanshu beae4a07c6 Fix: Update Product Prices was one was used instead of all in query. 2021-08-10 09:31:08 +05:30
tanshu 1ef7f98a07 Version Bump v9.6.0 2021-08-10 07:50:28 +05:30
tanshu 70c9d22bbc Fix: Void bill now saves old bill id 2021-08-10 07:50:08 +05:30
tanshu baefaeb228 Fix: Show would repeat the string n times instead of multiplying the integer 2021-08-09 23:07:04 +05:30
tanshu 5e0e99340e Fix: Beer Sale Report checking for wrong dates. 2021-08-09 21:02:51 +05:30
tanshu 5402d229f8 Fix: Product Sale Report would overwrite old value instead of adding to it in case the product had been updated and contained multiple product versions. 2021-08-07 17:03:16 +05:30
tanshu 2f440a4127 Feature: Sale Category also shows the products in it to make it easier to check for errors. 2021-08-06 08:23:36 +05:30
tanshu 0b3ec5da16 Fix: Product Detail / List would not show the quantity. 2021-08-06 08:20:18 +05:30
tanshu afa4fd4711 Fix: Import was not working as Customer Model changed and borked the initial commit of the database. Created a table in the function to remove dependence from the model.
Now poetry requires "poetry run" before commands to run them in the container. Updated for it.
2021-07-31 16:26:25 +05:30
tanshu eedc571ade Updated the docker and ansible files for setting up and building / running rasberry pi with the frank service
Still need to manually copy the new id_rsa created on the pi to knox.
2021-07-24 12:58:24 +05:30
tanshu 5cd0acc7a9 Feature: Added Happy Hour Discount in the bill display and changed Net amount to Gross Amount.
Changed the color of running / printed tables to a brighter colors.
2021-07-05 08:38:26 +05:30
tanshu c2483ae321 Fix: Edge cases where rounding due to changed quantities in printing was altering the final amount in decimals, at times resulted in final rounding to change up or down. 2021-07-05 08:28:38 +05:30
tanshu 8c5d941850 Fix: Kots with product quantity = 0 were allowed. So blank kots could be added. 2021-07-04 08:32:55 +05:30
tanshu 38ccaa167c Fix: If there were products with differing discounts in the final bill, the printed bill would show the wrong amount when it summed up the products. It would apply the discount rate of the first instance of the product too all of it. 2021-07-04 08:21:24 +05:30
tanshu 997e8e2dd0 Version Bump v9.5.0 2021-07-02 09:28:31 +05:30
tanshu c73b812911 Fix: Rare case where after bill is printed and nother kot added, the void bill kots have same time and a wrong kot can be printed.
This orders the kots by code, not time and so should take care of the issue
2021-07-02 09:28:12 +05:30
tanshu 5ece52ad55 Added option to view Voided bills 2021-07-02 09:05:37 +05:30
tanshu 17702a433b Fix: Bill Deactivate guard was running when printing kots / bills when items were added to new kot. 2021-07-02 09:05:07 +05:30
tanshu 1287d8f7ac Fix: Whitespaces around the password/username were causing login failures. 2021-06-30 07:12:53 +05:30
tanshu 0d41c0e345 Feature: Exit guard if bill is dirty is sales to prevent accidentally backing out of a bill with data. 2021-06-29 08:58:07 +05:30
tanshu fd2659eee5 Added device location to home screen. 2021-06-28 08:41:58 +05:30
tanshu db5f2731be Feature: Added a column called print in bill to the table customer.
This will prevent printing all customer's names and phone numbers in the bill in case of simple walkins.
This is a breaking change as there is schema changes in the database.
It also bolds the customers who are to be printed in the bill in the running tables list.
2021-06-28 08:41:32 +05:30
tanshu 143ddfb860 Version Bump v9.4.0 2021-06-27 08:08:43 +05:30
tanshu 7247ac34f4 Feature: Product Sale Report printing. 2021-06-27 08:08:20 +05:30
tanshu a1a1c10dff Fix: Remove legacy otp from login and show the right device id. 2021-06-26 09:14:36 +05:30
tanshu eb2f783b9e Feature: Tax report and consequently sale report should show tax broken up properly for taxes such as GST 2021-06-26 08:50:41 +05:30
tanshu da9d58025c Fix: Reports should now take the Timezone Offset into consideration 2021-06-26 08:49:26 +05:30
tanshu 77dad64eab Fix: Login errors were not helpful as error.details NOTICE THE "S" typo gobbled up the error message.
Also changed client to device
2021-06-26 08:46:22 +05:30
tanshu e0ef5e7b64 Fix: Rounding error prevented some bills from getting settled. This is related to the half even rounding in python. 2021-06-26 08:45:24 +05:30
tanshu ca3a599386 Fix: The bill settlement report was showing the wrong time, without taking into account the timezone. 2021-06-23 22:27:28 +05:30
tanshu 8a243fe387 Update frank to latest arq. We are having issues with duplicate kots being printed. This might be because the server arq is updated.
Also added the frank service and build frank script to the frank playbook so that new containers can be built on the device when ansible is not an option
2021-06-23 09:41:56 +05:30
tanshu 40aced4fa3 fix: When version was not bumped, deploy updated the version to blank 2021-06-22 05:37:53 +05:30
tanshu 567fb836b3 Fix: Kots should be sorted by date as a relationship of the voucher. This was causing them to appear in random order in sales. 2021-06-22 05:34:02 +05:30
tanshu 1c222872d2 Fix: Inactive Menu Categories should not show up in sales.
Trying to better cache dockerfile
2021-06-21 12:37:49 +05:30
tanshu 5d5439ae3b Version Bump v9.3.5 2021-06-21 12:04:59 +05:30
tanshu 4530a183c9 Fix: KOT Printing time was wrong. It showed the voucher time instead of kot time. 2021-06-21 12:04:02 +05:30
tanshu 7795f5ebef Version Bump v9.3.4 2021-06-21 10:48:19 +05:30
tanshu 15cd40d779 Fix: Allow linting to run without activating the poetry environment. 2021-06-21 10:47:33 +05:30
tanshu 27205f7790 Feature: Allow password change 2021-06-21 10:45:45 +05:30
tanshu 72b0e0e97d Chore: Crud 2021-06-19 12:35:12 +05:30
tanshu 6e284de75b Version Bump v9.3.3 2021-06-19 12:32:41 +05:30
tanshu 625a484bcd Chore: Updated poetry 2021-06-19 12:31:48 +05:30
tanshu 41431829f0 Version Bump v9.3.2 2021-06-19 10:51:48 +05:30
tanshu b47715bc22 Chore: Updated dependencies
Chore: Added the set -e command to deploy so that it quits on errors
2021-06-19 10:51:37 +05:30
tanshu 70d76f9951 Version Bump v9.3.1 2021-06-19 08:20:09 +05:30
tanshu d14250c0c7 Fix: Voucher Update would not filter out Zero quantity inventories. 2021-06-19 08:19:48 +05:30
tanshu 7cf6775ea1 Version Bump v9.3.0 2021-06-14 07:40:56 +05:30
tanshu d9a6c5d20f Fix: Open bill regex and the data sent by front-end was not compatible. 2021-06-14 07:40:43 +05:30
tanshu 83aa47b33a Version Bump v9.2.9 2021-06-13 18:40:03 +05:30
tanshu 66e541d50b Fix: Permission on Print Sale Report 2021-06-13 18:39:55 +05:30
tanshu 9961f2c3a1 Version Bump v9.2.8 2021-06-13 18:05:07 +05:30
tanshu 9f5a0b1209 Updated Dependencies 2021-06-13 18:04:55 +05:30
tanshu 1f516e75e6 Version Bump v9.2.7 2021-06-13 17:59:13 +05:30
tanshu 375e605e12 Fix: Dependency problem 2021-06-13 17:59:01 +05:30
tanshu defba7636c Version Bump v9.2.6 2021-06-13 17:54:46 +05:30
tanshu 12a361aff1 Fix: Could not print bill if discount permission not allowed 2021-06-13 17:54:04 +05:30
tanshu a4d389bba5 Chore: Upgraded to Angular v12 2021-05-16 10:31:18 +05:30
tanshu e1b824dce5 Version Bump v9.2.5 2021-04-27 07:23:47 +05:30
tanshu f78ad68235 Fix: The route to update sort order of Menu Categories was not written. 2021-04-27 07:22:32 +05:30
tanshu 59bab991fd Version Bump v9.2.4 2021-04-24 09:10:37 +05:30
tanshu 2f0ef397cf Fix: Disable pull to refresh in chrome. 2021-04-24 09:09:57 +05:30
tanshu 87402871a5 Version Bump v9.2.3 2021-04-24 00:47:41 +05:30
tanshu f410d4a69e Fix: Hopefully this will disable adroid chrome pull to refresh according to
https://developers.google.com/web/updates/2017/11/overscroll-behavior
2021-04-24 00:47:16 +05:30
tanshu 31e9071713 Chore: Forgot to add these files on dependency upgrade 2021-04-24 00:45:46 +05:30
tanshu 74e67a98e6 Fix: Editing printed bill will also print the new kot (if any) 2021-04-24 00:45:14 +05:30
tanshu e144175fe0 Version Bump v9.2.2 2021-04-24 00:06:33 +05:30
tanshu e66716710a Fix: Checking for product reductions would check the product version id and not the product id resulting in wrong error message that the quantitity cannot be reduced. 2021-04-24 00:05:06 +05:30
tanshu 76b27417d4 Chore: Updated dependencies 2021-04-23 13:17:14 +05:30
tanshu a29e9b8789 Version Bump v9.2.1 2021-04-23 01:00:59 +05:30
tanshu 2b22d6c4b5 Feature: When a new product is added, it adds modifiers which apply to the whole menu category. 2021-04-23 01:00:35 +05:30
tanshu 35a431d37e Fix: Do not store timezone in database. I am using UTC everywhere and use of timezones messes up date comparison. 2021-04-23 00:58:24 +05:30
tanshu 581d9ca507 Version Bump v9.2.0 2021-04-17 14:33:50 +05:30
tanshu e753ee625f Mohali docker playbook with env file. 2021-04-17 14:33:23 +05:30
tanshu 12f63cec73 Fix: Bill when printed was mangling up the taxes and amounts massively.
This was when we would make a dictionary of inventories and update quantities.
2021-04-17 14:32:55 +05:30
tanshu 2612da063b Version Bump v9.1.3 2021-04-15 20:13:20 +05:30
tanshu 438e99b6b0 Fix: Section Printer update would keep adding multiple default rows.
Fix: Kot select printer would have 1-2 rows, one with the null menu category and another (maybe) with the correct menu category.
2021-04-15 20:12:52 +05:30
tanshu 862892d7a1 Version Bump v9.1.2 2021-04-12 09:02:53 +05:30
tanshu d92cd295e1 Fix: Product list would not show as MenuCategoryLink needs list of products 2021-04-12 09:01:46 +05:30
tanshu 0477fde460 Version Bump v9.1.1 2021-04-10 08:57:43 +05:30
tanshu 9c0936e772 Fix: Didn't convert a few queries to 2.0 format. Caught some error with mypy, but don't know if that approach is usable. 2021-04-10 08:57:04 +05:30
tanshu ac0b8d2565 Version Bump v9.1.0 2021-04-09 09:34:37 +05:30
tanshu 4e827c2bd0 Feature: Added ability to change / choose pax once the table is running. 2021-04-09 09:33:53 +05:30
tanshu 4448869652 Fix: Menu category would only show active ones. 2021-04-09 09:32:08 +05:30
tanshu 978f0fcdff Chore: Move to sqlalchemy 2.0 querying style using the future flags on sqlalchemy 1.4 2021-04-09 09:31:36 +05:30
tanshu c0a4956fd4 Version Bump v9.0.5 2021-04-06 09:33:39 +05:30
tanshu b9c3b7eda5 Fix: Running tables view was showing wrong datetime. 2021-04-06 09:33:12 +05:30
tanshu 00730c1463 Hack: During import increase Tax rate from 2.5% to 5% for GST. 2021-04-06 09:24:53 +05:30
tanshu 32c74c65fc Version Bump v9.0.4 2021-04-06 08:51:17 +05:30
tanshu 07d13898c3 Fix: Dates in old imported data should be at UTC time.
Fix: Use utc now for dates to have consistent dates across timezones.
2021-04-06 08:50:37 +05:30
tanshu 823808d402 Version Bump v9.0.3 2021-04-05 08:21:31 +05:30
tanshu 54d205eebe Fix: Printer edit shows the proper hex values in printer edit and import the right hex value from old database 2021-04-05 08:20:45 +05:30
tanshu e1ed264674 Version Bump v9.0.2 2021-04-02 18:53:56 +05:30
tanshu 91451eebc9 Feature: Guestbook phone autosearch also searches names. 2021-04-02 18:52:22 +05:30
tanshu 78dcd6b92a Version Bump v9.0.1 2021-04-02 18:30:58 +05:30
tanshu 4c867138e2 Fix: Kots had the wrong time. 2021-04-02 18:30:42 +05:30
tanshu c3df28eb64 Version Bump v9.0.0 2021-04-02 14:34:38 +05:30
tanshu 73850560aa Customer discount with prefill discount in sales. 2021-04-02 14:34:07 +05:30
tanshu faf83f9356 Version Bump v8.2.0 2021-04-02 07:00:16 +05:30
tanshu 31ae068cab Worker container is not created anymore as this functionality has moved to frank. 2021-04-02 06:59:42 +05:30
tanshu 97579ea9d3 Dates and times should now be handled properly once the server is set on UTC time and the proper timezone offset environment variable is set. 2021-04-02 06:58:38 +05:30
tanshu 0da16e9548 Feature: Added the customer module to list / edit customers. This is needed to add the customer discount functionality 2021-04-02 06:34:31 +05:30
tanshu 214c40fbde Version Bump v8.1.0 2021-03-26 08:55:20 +05:30
tanshu d5b4dfeaca Feature: The guestbook now autocompletes on phone number 2021-03-26 08:54:31 +05:30
tanshu 3705ceb95b Search Added to Product List. 2021-03-26 08:01:30 +05:30
tanshu 0046c4fdd1 Chore: Updated the version bump script to also update the version on frank toml 2021-03-26 07:33:29 +05:30
tanshu 47381527c2 Feature: Added feature to click a name in receive payment to fill in the amount in that column 2021-03-26 07:20:51 +05:30
tanshu 095f4fe473 Fix: Reprint was not physically printing the bill. 2021-03-26 07:19:57 +05:30
tanshu 7ff546a442 Removed dangling file 2021-03-26 07:19:34 +05:30
tanshu 11600de3a8 Version Bump v8.0.0 2021-03-24 01:47:04 +05:30
tanshu f4caa19bb2 Moved printing into its own separate project called frank. It also has its own toml and deployement system.
It also works on a raspberry pi
2021-03-24 01:43:43 +05:30
tanshu 0b30ce258c Version Bump v7.3.0 2021-03-20 08:06:20 +05:30
tanshu 669821a643 Using Half-Round-Even rounding in the bill amounts as this is what python round uses.
When the bill amounts were Odd number + .5, payment could not be received as javascript rounded it up, but python rounded it down.
We are now using the python rounding (Half Round Even / Banker's Rounding) in the bill service.
2021-03-20 08:05:50 +05:30
tanshu 0775d12271 Version Bump v7.2.0 2021-03-19 08:06:54 +05:30
tanshu c29fde0cb0 Printer Cut Code should be properly handled now in editing as well.
Worker will retry a failed job.
The worker was using the wrong queue name.
2021-03-19 08:06:37 +05:30
tanshu dc6e7fe467 Version Bump v7.1.2 2021-03-19 06:55:32 +05:30
tanshu 592f26af20 Fix: The server was also using the worker settings to communicate with redis.
This led to it using the default host and port as the backend .env files do not contain worker settings.
2021-03-19 06:54:55 +05:30
tanshu e99b8f3070 Version Bump v7.1.1 2021-03-18 21:04:09 +05:30
tanshu e8d178a248 Fix: ORM Borked when models were split into their own files and product could not find this relation 2021-03-18 18:03:28 +05:30
tanshu 088982c6d4 Printer import now assigns the cut code for TM-m30
Also, hard-coding of the cut string is removed.
Now need to see how it will be handled in update printer
2021-03-18 17:52:54 +05:30
tanshu 52943c7ee0 Made the import script more versatile 2021-03-18 17:20:48 +05:30
tanshu 1163989866 Updated Dockerfiles 2021-03-18 17:19:14 +05:30
tanshu 30685e10a8 Version Bump v7.1.0 2021-03-18 07:12:55 +05:30
tanshu 2374fd7347 Chore: Updated angular and other javascript dependencies 2021-03-18 07:11:41 +05:30
tanshu 4257db020d Version Bump v7.0.0 2021-03-17 10:02:17 +05:30
tanshu d851714136 Printing working now. The env file for the worker needs to know the printer name.
Then, the address can be used to print the data.
2021-03-17 09:43:19 +05:30
tanshu ea2bfb1c0c Chore: Moved individual models into their own files.
Chore: Updated dependencies.
2021-03-17 09:39:56 +05:30
tanshu 716c74c974 Version Bump v6.3.0 2020-12-25 11:47:35 +05:30
tanshu 41d4ef1200 Added feature: Split bill by Sales Category if no item is selected. 2020-12-25 11:47:03 +05:30
tanshu 6257313792 Version Bump v6.2.0 2020-12-24 12:59:44 +05:30
tanshu 161896154d Feature: Open bill using bill number 2020-12-24 12:58:46 +05:30
tanshu 98c75f66c9 Fix: Cancel bill printing if discount dialog is cancelled. 2020-12-24 09:26:34 +05:30
tanshu 7b327b4fda Feature: Discount will only update the sale types which have been updated in the form. 2020-12-24 09:26:05 +05:30
tanshu 565cffaef1 Fix: Kot move fixed. Kot id was missing on move. Also, voucher_type KOT as truthy not and would give bill not found error. 2020-12-24 09:10:55 +05:30
tanshu 7550ced477 Version Bump v6.1.5 2020-12-21 11:24:42 +05:30
tanshu b85e930ace Moved all auth schemas into their own files. Updated imports. 2020-12-21 11:18:58 +05:30
tanshu c6da3b9e38 Version Bump v6.1.4 2020-12-21 08:03:15 +05:30
tanshu 4386ee45b8 Fix: Add Tax was not working as TaxIn required a name with length more than 1. 2020-12-21 08:02:36 +05:30
tanshu f65c19ce1a Version Bump v6.1.3 2020-12-20 08:05:21 +05:30
tanshu 9b55d037f2 Fixed: Add new modifier category error 2020-12-20 08:04:53 +05:30
tanshu 4e3362c614 Version Bump v6.1.2 2020-12-19 10:43:03 +05:30
tanshu f4b85dcb41 Added scripts for easy deployment. 2020-12-19 10:42:20 +05:30
tanshu 89c93862bf Added links to Sales and Guest Book on the Nav Bar 2020-12-19 09:32:27 +05:30
tanshu 7bd46a946e Version Bump v6.1.1 2020-12-19 09:09:51 +05:30
tanshu 8bb6235e67 Fix: Receive Payment
Since the amount Behaviour Subject pipe was not being subscribed to
and the original Behaviour Subject as being subscribed,
 amountVal was not being populated. Now it will be.
This was creating problem where amount in receive payment was always 0
Added a check in the backend so that if any part of bill remains unsettled, it will give an error instead of silently accepting and removing the entry.
2020-12-19 09:09:10 +05:30
tanshu 36915b41c1 Version Bump v6.1.0 2020-12-18 13:25:34 +05:30
tanshu b70b01b726 Back to table / product group order changed to have the back button in the beginning
The bill now highlights the happy hour products.
2020-12-18 13:24:50 +05:30
tanshu f28cf1eea0 Split bill working along with all checks.
Update bill ensures that the total number of happy hour punches of a product in a bill are less than or equal to the regular punches
2020-12-18 13:24:05 +05:30
tanshu 608dde4619 Fix: Modifier Category was borking as it was sending the product version id instead of product id in modifier category info 2020-12-18 12:53:08 +05:30
tanshu 63f5871350 Version Bump v6.0.0 2020-12-16 22:40:36 +05:30
tanshu 6c83c74424 Chore: In overlord / sale / bill.service now the BillViewItem is just a view item
The data is kept as the original bill object and this view generated on every change. It has no sanctity.

To deal with the challenges of Selection of items in the bill.component.html created a bill selection item.
This is converted to string while checking else the selection model fails.

Feature: It now checks if Happy Hour items have equivalent regular items in each kot.
Feature: Discount won't apply to happy hour items.
Checks for both are both in front end and back end.
2020-12-16 22:34:41 +05:30
tanshu 28952402aa Fix: Loaded voucher was not showing H H name in Happy Hour products 2020-12-16 12:28:39 +05:30
tanshu 02e208cd4e Version Bump v5.4.0 2020-12-16 11:50:23 +05:30
tanshu e4500f0d46 Breaking: Discount is applicable on sale category and not on menu category
Fix the import, etc on this.
While entering discount in sale, it checks the max allowed.
2020-12-16 11:49:22 +05:30
tanshu e229ecefa2 Version Bump v5.3.1 2020-12-15 22:40:24 +05:30
tanshu 22de4f7624 Fix: Reports were not working as they were not adapted to the new Enums
Fix: The docker image borked on printing as it did not have the en_IN locale
Fix: Angular errored as it needed typescript < 4.1.0
Fix: chd yml had a typo in host name
2020-12-15 22:15:51 +05:30
tanshu 5a73387404 Version Bump v5.3.0 2020-12-14 18:27:10 +05:30
tanshu 4f5ba47d46 Added the docker files and ansible playbooks for two instances namely pkl and chd. 2020-12-14 17:58:00 +05:30
tanshu 294f53147f Version Bump: v5.2.0 2020-12-13 13:20:46 +05:30
tanshu afc58d566c Fix: Permissions for header-footer.
The persission Owner is only for Settle Option and Header / Footer
2020-12-13 13:19:42 +05:30
tanshu 3ad73dff1c Feature: Now a tax can be broken up into multiple components while bill printing.
This is especially for GST. The format for the tax name is
"Display name 1 (Ratio eg. 1/2) ; Display name 1 (Ratio eg. 1/2)"
eg.
"SGST on Food @ 2.5% (1/2) ; CGST on Food @ 2.5% (1/2)"
or
"UTGST on Food @ 2.5% (1/2) ; CGST on Food @ 2.5% (1/2)"

Feature:
All report printing now uses local aware number formatting for Indian locale.
2020-12-13 13:14:19 +05:30
tanshu 27aa4d12a6 Settle Options are now stored in the Database and can be updated 2020-12-13 09:45:24 +05:30
tanshu d65379a068 Chore: ng lint using the recommended @angular-eslint style 2020-12-08 18:50:46 +05:30
tanshu 130d9197e5 Version bump v5.1.0 2020-11-25 12:39:05 +05:30
tanshu e16e1822b8 Fix: Modifier Categories was not showing the names of Menu Categories in Modifier Category Detail Component. This was because the Menu Category Link had only the id field.
Took this time to improve the Modifier route.
2020-11-25 12:38:22 +05:30
tanshu d9e0397b21 Version bump v5.1.0 2020-11-25 10:07:26 +05:30
tanshu b583b90756 Fully working with the rule no explicit any 2020-11-25 09:27:42 +05:30
tanshu 84535ca9bb Version bump to v5.0.2 2020-11-22 23:21:54 +05:30
tanshu f7b4b29d3a Fix: Bill Type was not being shown properly 2020-11-22 23:20:43 +05:30
tanshu e8ae39cd7a Fix: Angular won't compile as it requires typescript < 4.1.0 2020-11-22 10:58:18 +05:30
tanshu 3f49bea039 Version Bump to v5.0.0 2020-11-22 10:14:51 +05:30
tanshu 6567f560ab Updated to angular 11
Now compiling with strict mode in typescript
Need to error checking now
2020-11-22 10:13:37 +05:30
tanshu cabd6f2ea1 Version bump v4.3.0 2020-11-15 13:00:58 +05:30
tanshu 88f5b26948 Import marks inactive products as valid till one day before import 2020-11-15 12:58:12 +05:30
tanshu 163b40e9e5 Simplified some permissions
Renamed Accounts Audit to Audit
Renamed Machines to Devices as it made sense
Sections and Tables are now under Sections Permission
Guest Book is now under Customers Permission
Renamed Beer Consumption Report to Beer Sale Report
Fix: Move Kot and Table to check what the final effect is
2020-11-15 12:26:24 +05:30
tanshu aead1f1b36 Version bump to v4.2.0 2020-11-13 13:24:55 +05:30
tanshu 2dd9f7d962 Guest book now shows table status and link to open table 2020-11-13 13:23:34 +05:30
tanshu ef1ee35b65 Version Bump v4.1.1 2020-11-13 09:35:33 +05:30
tanshu 67cb1e3d80 Fix: Save/Update voucher - timedelta was days instead of minutes
Fix: Product list for sale was sending product_version_id instead of version_id this was borking on products with multiple versions
Change: Update Product Prices initially does not show list. Only the date and Menu Category List
2020-11-13 09:34:49 +05:30
tanshu 9317150bde Version Bump v4.1.0 2020-11-12 12:33:26 +05:30
tanshu 5e180f48d5 Update Product Prices built 2020-11-12 12:32:16 +05:30
tanshu d5b2da4388 Product updates report. 2020-11-11 12:18:10 +05:30
tanshu 6f9394dc8d Chore: Start and Finish date in reports extracted to function 2020-11-11 10:09:07 +05:30
tanshu 8db9a7d757 Header / Footer editor added - Right now linked to products permission, which should be changed 2020-11-11 09:56:16 +05:30
tanshu 77fb8e8428 Did not add the docker entrypoint file
v4.0.1
2020-11-10 13:36:54 +05:30
tanshu de1b199da0 Version bump to v4.0.0 due to temporal products 2020-11-10 12:46:32 +05:30
tanshu de235f3d9e Product should now be fixed.
Need to allow later editing now.
2020-11-10 11:47:25 +05:30
tanshu a92726f5e6 Product list / detail / router fully working.
Need to test the sale / reports
2020-11-09 12:44:27 +05:30
tanshu 4aaa3fc72b Import data also adds all permissions to owner role 2020-11-08 20:01:27 +05:30
tanshu 94b63e4385 Product List and Query working 2020-11-08 19:09:31 +05:30
tanshu 0c9ebd29d7 Initial setup for temporal products table.
Updated the model and the data import.
Now for all the routers and then update products
2020-11-07 21:09:58 +05:30
tanshu 9ea143d233 version bump
v3.1.0
2020-10-29 07:46:11 +05:30
tanshu 4108040cdd new tag v3.1.0
Fix:
  Company references left after the field was removed
2020-10-29 07:32:04 +05:30
tanshu 969b774a25 Fixed Worker 2020-10-28 11:02:35 +05:30
tanshu f1a360a4ea Removed product_group_modifier import 2020-10-28 02:18:33 +05:30
tanshu 1e69c8eeeb Importing now working
except u-product_group_modifier. It does not fit in the new scheme. Will remove in next commit so that it is added to log if needed later.
2020-10-28 02:16:55 +05:30
tanshu f8683cf080 Devices have replaced Clients for authentication as well as location using sections.
Printing of reports done.
Main section is now a fixture

User and Devices list gives last login details.
2020-10-27 16:59:24 +05:30
tanshu cbc2f29e29 Bump to version 3.0.0
Printing done on:
 Cashier Report
 Discount Report
 Sale Report
2020-10-27 12:13:17 +05:30
tanshu 00fe2410b7 blacked, isorted, flake8ed and moved to arq/redis from celery/rabbitmq 2020-10-24 11:53:12 +05:30
tanshu 1d6baa9d1d fix: Worker command 2020-10-14 09:29:44 +05:30
tanshu fc6eebed19 Infrastructure to build the worker docker and run it. 2020-10-14 08:19:42 +05:30
tanshu 99f5f09c17 Bump to v2.0.0 2020-10-11 21:02:51 +05:30
tanshu b7f382cac8 Bill change should be working
Reduce quantity should be working.
2020-10-11 20:53:43 +05:30
tanshu 73f83f1aa7 Show proper error messages on Save Bill / Save Kot 2020-10-11 11:59:37 +05:30
tanshu 7baf79c3fa Fix: ValueErrors should be HTTPException 2020-10-11 11:20:12 +05:30
tanshu 9ecf0b5cc9 Display frontend / backend version at the homescreen 2020-10-11 11:13:06 +05:30
tanshu d677cfb1ea Blacked and isorted the python files
Prettied and eslinted the typescript/html files
2020-10-11 10:56:29 +05:30
tanshu b31db593c2 Updated to angular 10
Moved to eslint for linting
Added prettier for formatting
Fixed minor errors
2020-10-11 09:34:35 +05:30
tanshu 066d43a373 Moved barker one level down and using poetry for setup 2020-10-11 09:06:51 +05:30
tanshu 3f2a06c2cf Feature:
Print kot and print bill are working now using celery

Now also need to install RabbitMQ for clery to work
2020-10-01 11:43:43 +05:30
tanshu aaac5efea2 Feature:
Basic infra to print stuff, not working
2020-09-29 11:18:24 +05:30
tanshu 046504e097 Ported:
Change (Reprint bill)

Chore:
  Refactored save voucher so that the save function can be reused in change voucher route

Fix:
  Show voucher sent modifiers in a wrong format
  Inventory schema used rate instead of price
2020-09-29 11:03:03 +05:30
tanshu f8778fee74 Ported:
Split bill done - TODO: If the products have different price due to paid modifiers
2020-09-28 08:09:38 +05:30
tanshu cf34c2b855 Ported:
Table Merge/Move and Kot Merge/Move
2020-09-25 08:50:39 +05:30
tanshu bf09471e9e Fix:
Discount button was greyed out due to wrong check.
  Show was sending customer as empty dictionary which triggered an error in input as id was a required column
2020-09-24 09:18:58 +05:30
tanshu 578385f866 Chore:
Deduplicated the settle options function for save/update/void/receive payment

Ported:
  Void
  Receive payment
2020-09-24 08:47:09 +05:30
tanshu 6c06271315 Ported:
Guest Book
  Save bill
  Update bill
  Show bill

Fix:
  Tax details was sending the rate multiplied by 100 but not dividing it on save/update

Feature:
  Updated the auth service to check local storage for the latest user token
2020-09-24 07:39:46 +05:30
tanshu b19d8cc030 Ported:
Guest Book

Chore:
  Added New Day Offset and Timezone Offset in settings so that they can be shared consistently and also set in runtime.
2020-09-22 10:53:31 +05:30
tanshu cabdd505e0 Ported:
Customer
  Device
  Role
  Settings
2020-09-22 08:12:41 +05:30
tanshu 7b6c09cea3 All Reports done!! 2020-06-17 09:57:41 +05:30
tanshu 938bb67e0a All Masters Done!! 2020-06-15 21:40:12 +05:30
tanshu fdfd3dcbfb Added: Alembic for migrations
Moving from Pyramid to FastAPI
2020-06-14 18:43:10 +05:30
Amritanshu 0c0a2990a8 In case of a table with no guest, it will ask for pax 2019-08-26 15:11:28 +05:30
Amritanshu a12f093828 Renamed void_reason to reason so that we can store the employee name in case of Staff and NC reason in case of NC bills.
Settling NC and Staff bills now asks for Staff name / NC reason
2019-08-25 23:22:50 +05:30
Amritanshu 6d0f30503a Renamed Report permissions to make more sense
Removed the void and reprints report and added it to the bill settlement report
updated the import to add a new sql to be executed later to update the settlements and report permission names
Export works on all reports
2019-08-25 15:08:59 +05:30
Amritanshu 05860fb0b9 Added nav-bar to show home link and user status 2019-08-22 14:15:02 +05:30
Amritanshu 04fb6dfcab Fix: Move table was checking !x instead of !!x and so was not working
Added checks to void bill, split bill and move table
2019-08-21 17:02:06 +05:30
Amritanshu ee54e36dda Added routes for serving index.html for various routes.
Depreciated: Customers
TODO: Reprint, Setting, Change (ie. voucher void and reprint)
2019-08-21 16:16:46 +05:30
Amritanshu 461db9d8fa Fix: Wrong permission of sales 2019-08-21 15:40:46 +05:30
Amritanshu 2c0c91b136 Modifiers popup not scrolling fixed 2019-08-21 15:39:01 +05:30
Amritanshu 992a6b6767 Fix: Checkout was broken 2019-08-21 14:37:46 +05:30
Amritanshu 5603c5d8fb Fix: Product units 2019-08-21 14:27:11 +05:30
Amritanshu fd1111f378 Display units in Sale / Product and don't show brackets when no units entered 2019-08-21 14:13:53 +05:30
Amritanshu b898e26dc6 Added the final missing Voids or Reprints report aslo.
Ready for beta 1.
Removed voucher.is_void in favour voucher_type == void
2019-08-21 13:23:41 +05:30
Amritanshu 2b04b624b3 Discount Report Done 2019-08-21 12:31:52 +05:30
Amritanshu 8f6c5930ee Beer consumption report done 2019-08-21 11:12:30 +05:30
Amritanshu 243c3cb280 Bill Settlement Report Done (Bill Details)
TODO: Rename Bill Details Permission to Bill Settlement Report
2019-08-21 10:02:28 +05:30
Amritanshu 0f00d96dc9 Product Sale Module done (Sales Detail) -- need to rename the permission 2019-08-21 09:27:19 +05:30
Amritanshu 241568622e Feature: Tax Analysis 2019-08-21 08:32:12 +05:30
Amritanshu abc29844d3 Fixed a lot of permissions 2019-08-21 00:07:46 +05:30
Amritanshu 70d31fea5e Feature: Sale Analysis is working
Fix: Cashier Checkout multiple entries
2019-08-20 23:02:42 +05:30
Amritanshu c6b907061e Updated version 2019-08-20 14:44:12 +05:30
Amritanshu 63c7a541dc Fix: Importing was messed up
Fix: Cashier checkout kept changing the date
2019-08-20 14:42:43 +05:30
Amritanshu aa81743ce7 Make the build distributable 2019-08-20 08:41:44 +05:30
1003 changed files with 43734 additions and 16642 deletions

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
{
"mcpServers": {
"postgres-db": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"${POSTGRES_URL}"
],
"env": {
"POSTGRES_URL": "${env:SQLALCHEMY_DATABASE_URI}"
}
},
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"."
]
},
"angular-cli": {
"command": "npx",
"args": [
"-y",
"@angular/cli",
"mcp"
]
}
}
}
+30
View File
@@ -0,0 +1,30 @@
# Project Architecture & Guidelines
## Monorepo Layout
- **Backend**: `./barker` (Python, managed via `uv`)
- **Frontend**: `./bookie` (Angular, managed via `npm`/`ng`)
- **Environment**: Root `.env` file containing shared/database variables.
---
## Execution Rules & Commands
### Backend (`/barker`)
- **Working Directory**: Always execute backend commands within `./barker` OR pass `--directory barker` to `uv`.
- **Package & Script Management**:
- Use `uv` exclusively. Never run bare `pip` or global `python`.
- Run scripts/tests: `uv run python <script>` or `uv run pytest` from inside `./barker`.
- Manage dependencies: `uv add <package>` or `uv remove <package>`.
- **Environment**: The root `.env` file (`../.env` relative to `barker`) contains the database connection string and secret keys.
### Frontend (`/bookie`)
- **Working Directory**: Always execute frontend commands within `./bookie`.
- **Commands**:
- Run dev server: `npm start` or `ng serve`
- Code generation: `ng g c components/<name>` (prefer standalone components)
- Testing: `ng test --watch=false`
- Build: `ng build`
### Database (PostgreSQL)
- Database settings are defined in the root `.env`.
- Inspect schema, run migrations, or check types strictly via database tools or `uv run` scripts inside `./barker`.
+117
View File
@@ -0,0 +1,117 @@
---
trigger: glob
globs: ./bookie/**/**
---
# Persona
You are a dedicated Angular developer who thrives on leveraging the absolute latest features of the framework to build cutting-edge applications. You are currently immersed in Angular v20+, passionately adopting signals for reactive state management, embracing standalone components for streamlined architecture, and utilizing the new control flow for more intuitive template logic. Performance is paramount to you, who constantly seeks to optimize change detection and improve user experience through these modern Angular paradigms. When prompted, assume You are familiar with all the newest APIs and best practices, valuing clean, efficient, and maintainable code.
## Examples
These are modern examples of how to write an Angular 20 component with signals
```ts
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
@Component({
selector: '{{tag-name}}-root',
templateUrl: '{{tag-name}}.html',
})
export class {{ClassName}} {
protected readonly isServerRunning = signal(true);
toggleServerStatus() {
this.isServerRunning.update(isServerRunning => !isServerRunning);
}
}
```
```css
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
button {
margin-top: 10px;
}
}
```
```html
<section class="container">
@if (isServerRunning()) {
<span>Yes, the server is running</span>
} @else {
<span>No, the server is not running</span>
}
<button (click)="toggleServerStatus()">Toggle Server Status</button>
</section>
```
When you update a component, be sure to put the logic in the ts file, the styles in the css file and the html template in the html file.
## Resources
Here are the some links to the essentials for building Angular applications. Use these to get an understanding of how some of the core functionality works
https://angular.dev/essentials/components
https://angular.dev/essentials/signals
https://angular.dev/essentials/templates
https://angular.dev/essentials/dependency-injection
## Best practices & Style guide
Here are the best practices and the style guide information.
### Coding Style guide
Here is a link to the most recent Angular style guide https://angular.dev/style-guide
### TypeScript Best Practices
- Use strict type checking
- Prefer type inference when the type is obvious
- Avoid the `any` type; use `unknown` when type is uncertain
### Angular Best Practices
- Always use standalone components over `NgModules`
- Do NOT set `standalone: true` inside the `@Component`, `@Directive` and `@Pipe` decorators
- Use signals for state management
- Implement lazy loading for feature routes
- Use `NgOptimizedImage` for all static images.
- Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead
### Components
- Keep components small and focused on a single responsibility
- Use `input()` signal instead of decorators, learn more here https://angular.dev/guide/components/inputs
- Use `output()` function instead of decorators, learn more here https://angular.dev/guide/components/outputs
- Use `computed()` for derived state learn more about signals here https://angular.dev/guide/signals.
- Prefer inline templates for small components
- Prefer Reactive forms instead of Template-driven ones
- Do NOT use `ngClass`, use `class` bindings instead, for context: https://angular.dev/guide/templates/binding#css-class-and-style-property-bindings
- Do NOT use `ngStyle`, use `style` bindings instead, for context: https://angular.dev/guide/templates/binding#css-class-and-style-property-bindings
### State Management
- Use signals for local component state
- Use `computed()` for derived state
- Keep state transformations pure and predictable
- Do NOT use `mutate` on signals, use `update` or `set` instead
### Templates
- Keep templates simple and avoid complex logic
- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
- Use the async pipe to handle observables
- Use built in pipes and import pipes when being used in a template, learn more https://angular.dev/guide/templates/pipes#
### Services
- Design services around a single responsibility
- Use the `providedIn: 'root'` option for singleton services
- Use the `inject()` function instead of constructor injection
+17
View File
@@ -0,0 +1,17 @@
---
name: angular-signals
description: Writes modern Angular code using Standalone Components and Signals. Use whenever generating frontend UI or state logic.
---
# Modern Angular Standards
## Architecture
- NEVER generate or use `NgModule`.
- ALL components, directives, and pipes must be `standalone: true`.
## State Management (Signals)
- Prefer Angular Signals (`signal()`, `computed()`, `effect()`) for synchronous, local component state instead of `BehaviorSubject`.
- Use the new Signal-based inputs and outputs: `input()` and `output()` instead of `@Input()` and `@Output()`.
- Use standard RxJS strictly for asynchronous streams (e.g., HTTP requests, router events).
## Control Flow
- Use the new built-in control flow (`@if`, `@for`, `@switch`) instead of structural directives (`*ngIf`, `*ngFor`).
@@ -0,0 +1,16 @@
---
name: fastapi-pydantic-v2
description: Writes modern FastAPI endpoints using Pydantic v2 syntax and Annotated dependencies. Use whenever generating or refactoring API routes.
---
# FastAPI & Pydantic v2 Standards
## Pydantic v2 Rules
- NEVER use `.dict()` or `.json()`. ALWAYS use `.model_dump()` and `.model_dump_json()`.
- NEVER use `@validator` or `@root_validator`. ALWAYS use `@field_validator` and `@model_validator(mode="before|after")`.
- NEVER use `schema_extra`. Use `json_schema_extra` inside the `model_config` dict.
- Use `pydantic_settings.BaseSettings` for all environment variable configurations.
## FastAPI Rules
- ALWAYS use `typing.Annotated` for dependency injection (e.g., `db: Annotated[AsyncSession, Depends(get_db)]`).
- Do not use `def` for endpoints that perform I/O; always use `async def`.
- Raise `fastapi.HTTPException` for expected client errors instead of returning standard dictionaries.
+19
View File
@@ -0,0 +1,19 @@
---
name: sqlalchemy-async
description: Writes SQLAlchemy 2.0 async database queries using Psycopg3. Use whenever interacting with the PostgreSQL database.
---
# SQLAlchemy 2.0 Async Standards
## Model Definitions
- ALWAYS use SQLAlchemy 2.0 Declarative Base with `Mapped` and `mapped_column`.
- Example: `id: Mapped[int] = mapped_column(primary_key=True)`
- NEVER use the old `Column(Integer, primary_key=True)` syntax.
## Querying
- NEVER use `session.query()`. This is legacy 1.x syntax.
- ALWAYS use `sqlalchemy.select()` combined with `await session.execute(stmt)`.
- Extract results using `.scalars().all()` or `.scalar_one_or_none()`.
## Session Management
- Use `sqlalchemy.ext.asyncio.AsyncSession`.
- Ensure all database I/O is awaited.
+30
View File
@@ -0,0 +1,30 @@
---
name: strict-mypy
description: Enforces strict MyPy type checking conventions matching the project's pyproject.toml configuration. Use whenever writing or modifying Python code.
---
# Strict MyPy Typing Standards
This project uses a highly strict MyPy configuration (`strict = true`). When writing or refactoring Python code, you MUST adhere to the following rules to ensure the code passes type checking:
## 1. Absolute Signature Completeness
- EVERY function and method must have fully typed arguments and a return type (`disallow_untyped_defs`, `disallow_incomplete_defs`).
- ALWAYS specify `-> None` for functions and methods that do not return a value.
- NEVER leave type hints partially defined (e.g., typing only some arguments).
- Decorators must be completely type-hinted using `typing.Callable` or `typing.ParamSpec` (`disallow_untyped_decorators`).
## 2. No Implicit Optionals
- `arg: str = None` is strictly forbidden.
- You MUST explicitly union with None: `arg: str | None = None` (`no_implicit_optional`).
## 3. Strict Generics
- NEVER use bare collections as types.
- ALWAYS specify the inner types: use `list[str]`, `dict[str, int]`, or `tuple[int, ...]` instead of bare `list`, `dict`, or `tuple` (`disallow_any_generics`).
## 4. Restrictions on `Any` and Subclassing
- Do not subclass from untyped external libraries or `Any` (`disallow_subclassing_any`).
- Avoid using external types that have no stubs and resolve to `Any` (`disallow_any_unimported`).
- Refrain from returning `Any` to avoid contaminating the type inference of calling functions (`warn_return_any`).
## 5. Pydantic Strictness
- `pydantic.mypy` is active with strict initialization.
- When instantiating Pydantic models, ensure you are passing exactly the expected typed arguments, as extra or untyped arguments will fail type checks (`init_typed = true`, `init_forbid_extra = true`).
+61
View File
@@ -0,0 +1,61 @@
# Version control & IDEs
.git
.gitignore
.vscode
.idea
.agents
.gemini
# Build artifacts & compiled outputs
frontend
dist
**/dist
out-tsc
**/out-tsc
bazel-out
# Dependencies & framework caches
node_modules
**/node_modules
.angular
**/.angular
.nx
**/.nx
.sass-cache
**/.sass-cache
# Python virtual environments & caches
.venv
**/__pycache__
**/.mypy_cache
**/.ruff_cache
**/.pytest_cache
*.py[cod]
*$py.class
*.egg-info
# Testing & coverage
coverage
**/coverage
.coverage
htmlcov
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment & local deployment/dev files
.env*
!.env.example
ansible
deploy.sh
lint.sh
version_bump.sh
Makefile
README.md
CHANGES.md
typings
.DS_Store
Thumbs.db
+29
View File
@@ -0,0 +1,29 @@
HOST=0.0.0.0
PORT=9995
LOG_LEVEL=INFO
DEBUG=true
SQLALCHEMY_DATABASE_URI=postgresql+psycopg://postgres:123456@localhost:5432/petty
MODULE_NAME=barker.main
PROJECT_NAME=barker
MAX_WORKERS=4
NATS_URL=nats://localhost:4222
SUBJECT="barker.print"
STREAM="barker_print_stream"
# openssl rand -hex 32
SECRET_KEY=611f9393e58f85521d16d9497bfd847ced70f2b99e12a9c6af40b7bd05cc7b1d
# openssl rand -hex 5
MIDDLEWARE_SECRET_KEY=c4afceca1a
ALGORITHM=HS256
JWT_TOKEN_EXPIRE_MINUTES=30
NEW_DAY_OFFSET_MINUTES =420
TIMEZONE_OFFSET_MINUTES=330
ALEMBIC_LOG_LEVEL=INFO
ALEMBIC_SQLALCHEMY_LOG_LEVEL=WARN
FASTAPI_ENV=development
+50 -7
View File
@@ -1,9 +1,52 @@
*.pyc
env
.project
.pydevproject
*/__pycache__/
# IDEs & System Files
.idea/
.DS_Store
Thumbs.db
# Build outputs
frontend/
dist/
**/dist/
out-tsc/
**/out-tsc/
bazel-out/
# Angular & Nx caches
.angular/
**/.angular/
.nx/
**/.nx/
.sass-cache/
# Dependencies
node_modules/
**/node_modules/
# Python & Virtual Environments
.venv/
**/__pycache__/
*.py[cod]
*$py.class
*.egg-info/
DB/*.sql
barker/static/
.mypy_cache/
**/.mypy_cache/
.ruff_cache/
**/.ruff_cache/
.pytest_cache/
**/.pytest_cache/
# Testing & Coverage
coverage/
**/coverage/
.coverage
htmlcov/
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Ignore Pyright-generated stubs
*.pyi
typings/
+1
View File
@@ -0,0 +1 @@
3.14
+14
View File
@@ -0,0 +1,14 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": [
"ms-vscode-remote.remote-wsl",
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.debugpy",
"charliermarsh.ruff",
"docker.docker",
"angular.ng-template",
"dbaeumer.vscode-eslint",
"tombi-toml.tombi"
]
}
+43
View File
@@ -0,0 +1,43 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "Python: FastAPI",
"type": "debugpy",
"request": "launch",
"module": "barker",
"args": [],
"cwd": "${workspaceFolder}/barker",
"justMyCode": true,
"envFile": "${workspaceFolder}/.env"
},
{
"name": "Python: FastAPI (Live Reload)",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": [
"barker.main:app",
"--host",
"0.0.0.0",
"--port",
"9995",
"--reload"
],
"cwd": "${workspaceFolder}/barker",
"subProcess": true,
"justMyCode": true,
"envFile": "${workspaceFolder}/.env"
}
]
}
+9
View File
@@ -0,0 +1,9 @@
{
// For more information, visit: https://angular.dev/ai/mcp
"servers": {
"angular-cli": {
"command": "npx",
"args": ["-y", "@angular/cli", "mcp"]
}
}
}
+79
View File
@@ -0,0 +1,79 @@
{
"python.defaultInterpreterPath": "barker/.venv/bin/python",
"python.terminal.useEnvFile": true,
"python.venvFolders": [
"barker/.venv",
"frank/.venv",
".venv"
],
"python.venvPath": "${workspaceFolder}/barker",
"python.terminal.activateEnvironment": true,
// Quality-of-life
/* --- Pylance: keep IntelliSense, disable type checking --- */
// "python.analysis.typeCheckingMode": "off",
// "python.analysis.diagnosticMode": "openFilesOnly",
"python.analysis.typeCheckingMode": "basic",
"python.analysis.extraPaths": [
"${workspaceFolder}/barker",
"./barker"
],
"python.autoComplete.extraPaths": [
"${workspaceFolder}/barker",
"./barker"
],
// Ruff (you have ruff installed via uv)
"editor.formatOnSave": true,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff"
},
"ruff.enable": true,
"files.eol": "\n",
"eslint.workingDirectories": [
{
"directory": "bookie",
"changeProcessCWD": true
}
],
"makefile.configureOnOpen": false,
"files.exclude": {
"**/__pycache__": true,
"**/.mypy_cache": true,
"**/.ruff_cache": true,
"**/.pytest_cache": true,
".idea": true,
"**/node_modules": true,
"typings": true,
"frontend": true,
"**/.angular": true,
"**/.nx": true,
"**/out-tsc": true,
"**/dist": true,
"**/.sass-cache": true,
"**/coverage": true
},
"search.exclude": {
"**/__pycache__": true,
"**/.mypy_cache": true,
"**/.ruff_cache": true,
"**/.pytest_cache": true,
".idea": true,
"**/node_modules": true,
"**/poetry.lock": true,
"**/package-lock.json": true,
"typings": true,
"frontend/**": true,
"**/.angular/**": true,
"**/.nx/**": true,
"**/out-tsc/**": true,
"**/dist/**": true,
"**/.sass-cache/**": true,
"**/coverage/**": true
},
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "always",
"source.fixAll.eslint": "always"
},
"python-envs.defaultEnvManager": "ms-python.python:venv",
"python-envs.defaultPackageManager": "ms-python.python:pip",
"python-envs.pythonProjects": []
}
+48
View File
@@ -0,0 +1,48 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"options": {
"cwd": "${workspaceFolder}/bookie"
},
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"options": {
"cwd": "${workspaceFolder}/bookie"
},
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}
View File
File renamed without changes.
-28
View File
@@ -1,28 +0,0 @@
"\Program Files\PostgreSQL\11\bin\dropdb.exe" -U postgres petty
"\Program Files\PostgreSQL\11\bin\createdb.exe" -U postgres petty
..\env\Scripts\initdb.exe ..\development.ini
..\env\Scripts\fixtures.exe ..\development.ini
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < a-Roles.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < b-Permissions.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < c-RolePermissions.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < d-Users.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < e-UserRoles.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < f-Customers.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < g-FoodTables.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < h-Taxes.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < i-MenuCategories.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < j-SaleCategories.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < k-Products.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < l-Modifiers.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < m-Sections.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < n-Printers.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < o-SectionPrinters.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < p-Vouchers.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < q-Kots.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < r-Inventories.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < s-InventoryModifiers.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < t-Overview.sql
REM "\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < u-ProductGroupModifiers.sql
REM "\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < v-Reprints.sql
REM "\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < w-Settings.sql
"\Program Files\PostgreSQL\11\bin\psql.exe" -U postgres petty < x-VoucherSettlements.sql
+24 -29
View File
@@ -1,36 +1,31 @@
@echo off
call:copyQuery a-Roles "SELECT 'INSERT INTO roles(id, name) VALUES (''' + CAST(GroupID AS Nvarchar(36)) + ''', ''' + Name + ''');' FROM Test.dbo.Auth_Groups;"
call:copyQuery b-Permissions "SELECT 'INSERT INTO permissions(id, name) VALUES (''' + CAST(RoleID AS Nvarchar(36)) + ''', ''' + Name + ''');' FROM Test.dbo.Auth_Roles;"
call:copyQuery c-RolePermissions "SELECT 'INSERT INTO role_permissions(id, permission_id, role_id) VALUES (''' + CAST(RoleGroupID AS Nvarchar(36)) + ''', ''' + CAST(RoleID AS Nvarchar(36)) + ''', ''' + CAST(GroupID AS Nvarchar(36)) + ''');' FROM Test.dbo.Auth_RoleGroups;"
call:copyQuery d-Users "SELECT 'INSERT INTO users(id, name, password, locked_out) VALUES (''' + CAST(UserID AS Nvarchar(36)) + ''', ''' + Name + ''', ''' + Password + ''', ' + CASE WHEN LockedOut = 1 THEN 'true' ELSE 'false' END + ');' FROM Test.dbo.Auth_Users;"
call:copyQuery e-UserRoles "SELECT 'INSERT INTO user_roles(id, user_id, role_id) VALUES (''' + CAST(UserGroupID AS Nvarchar(36)) + ''', ''' + CAST(UserID AS Nvarchar(36)) + ''', ''' + CAST(GroupID AS Nvarchar(36)) + ''');' FROM Test.dbo.Auth_UserGroups;"
call:copyQuery f-Customers "SELECT 'INSERT INTO customers(id, name, address, company, phone) VALUES (''' + CAST(CustomerID AS Nvarchar(36)) + ''', ''' + REPLACE(Name, '''', '''''') + ''', ''' + COALESCE(Address, 'null') + ''', '''', ' + CASE WHEN Phone = '' THEN 'null' ELSE '''' + COALESCE(Phone, Name) + '''' END + ');' FROM Test.dbo.Customers WHERE CustomerID != '2c716f4b-0736-429a-ad51-610d7c47cb5e';"
call:copyQuery g-FoodTables "SELECT 'INSERT INTO food_tables(id, name, seats, is_active, section_id, sort_order) VALUES (''' + CAST(FoodTableID AS Nvarchar(36)) + ''', ''' + Name + ''', 0, ' + CASE WHEN IsActive = 1 THEN 'true' ELSE 'false' END + ', ''3f13f6e7-dc76-4fca-8fdb-b2bbf29b35df'', ' + CAST(SortOrder AS Nvarchar(36)) + ');' FROM Test.dbo.FoodTables;"
call:copyQuery h-Taxes "SELECT 'INSERT INTO taxes(id, name, rate, is_fixture) VALUES ('''+ CAST(TaxID AS Nvarchar(36)) + ''', ' + '''' + REPLACE(Name, '''', '''''') + '''' + ', ' + CAST(Rate AS Nvarchar(36)) + ', false);' FROM Test.dbo.Taxes;"
call:copyQuery i-MenuCategories "SELECT 'INSERT INTO menu_categories(id, name, discount_limit, is_active, is_fixture, sort_order) VALUES (''' + CAST(ProductGroupID AS Nvarchar(36)) + ''', ''' + REPLACE(Name, '''', '''''') + ''', ' + CAST(DiscountLimit AS nvarchar(36)) + ', ' + CASE WHEN IsActive = 1 THEN 'true' ELSE 'false' END + ', false, ' + CAST(SortOrder AS Nvarchar(36)) + ');' FROM Test.dbo.ProductGroups;"
call:copyQuery j-SaleCategories "SELECT 'INSERT INTO sale_categories(id, name, tax_id) VALUES (''' + CAST(newid() AS Nvarchar(36)) + ''', ''' + REPLACE(pgo.GroupType, '''', '''''') + ''', ''' + CAST((select top 1 t.TaxID from Test.dbo.Taxes t inner join Test.dbo.Products p on t.TaxID = p.VatID inner join Test.dbo.ProductGroups pg on p.ProductGroupID = pg.ProductGroupID where pg.GroupType = pgo.GroupType) AS Nvarchar(36)) + ''');' from (select distinct pgx.GroupType from Test.dbo.ProductGroups pgx) as pgo;"
call:copyQuery k-Products "SELECT 'INSERT INTO products(id, name, units, menu_category_id, sale_category_id, price, has_happy_hour, is_active, is_not_available, sort_order, quantity) VALUES (''' + CAST(ProductID AS Nvarchar(36)) + ''', ' + '''' + REPLACE(Name, '''', '''''') + '''' + ', ' + '''' + Units + '''' + ', ' + '''' + CAST(ProductGroupID AS Nvarchar(36)) + '''' + ', ' + '(select id from sale_categories where name = ''' + (SELECT GroupType FROM Test.dbo.ProductGroups pgs WHERE pgs.ProductGroupID = Products.ProductGroupID) + ''')' + ', ' + CAST(Price AS Nvarchar(36)) + ', ' + CASE WHEN HasHappyHour = 1 THEN 'true' ELSE 'false' END + ', ' + CASE WHEN IsActive = 1 THEN 'true' ELSE 'false' END + ', ' + CASE WHEN IsNotAvailable = 1 THEN 'true' ELSE 'false' END + ', ' + CAST(SortOrder AS Nvarchar(36)) + ', ' + CAST(Quantity AS Nvarchar(36)) + ');' FROM Test.dbo.Products;"
call:copyQuery l-Modifiers "SELECT 'INSERT INTO modifiers(id, name, show_in_bill, is_active, modifier_category_id, price) VALUES (''' + CAST(ModifierID AS Nvarchar(36)) + ''', ' + '''' + REPLACE(Name, '''', '''''') + '''' + ', ' + CASE WHEN ShowInBill = 1 THEN 'true' ELSE 'false' END + ', true, 'e046ad33-dc65-4c78-8833-c3d3538d44c0', ' + CAST(Price AS Nvarchar(36)) + ');' FROM Test.dbo.Modifiers;"
call:copyQuery m-Sections "SELECT 'INSERT INTO sections(id, name) VALUES (''' + CAST(NewID() AS Nvarchar(36)) + ''', ''' + Location + ''');' FROM Test.dbo.PrintLocations GROUP BY Location;"
call:copyQuery n-Printers "SELECT 'INSERT INTO printers(id, name, address, cut_code) VALUES (''' + CAST(NewID() AS Nvarchar(36)) + ''', ''' + Printer + ''', ''' + Printer + ''', ''' + CutCode + ''');' FROM Test.dbo.PrintLocations GROUP BY Printer, CutCode;"
call:copyQuery o-SectionPrinters "SELECT 'INSERT INTO section_printers(id, menu_category_id, section_id, printer_id, copies) VALUES (''' + CAST(PrintLocationID AS Nvarchar(36)) + ''', ' + COALESCE('''' + CAST(ProductGroupID AS Nvarchar(36)) + '''','null') + ', ' + '(select id from sections where name = ''' + Location + ''')' + ', ' + '(select id from printers where name = ''' + Printer + ''')' + ', ' + CAST(Copies AS nvarchar(36)) + ');' FROM Test.dbo.PrintLocations;"
call:copyQuery p-Vouchers "SELECT 'INSERT INTO vouchers(id, date, pax, user_id, creation_date, last_edit_date, bill_id, food_table_id, customer_id, narration, is_void, void_reason, voucher_type, kot_id) VALUES (''' + CAST(VoucherID AS Nvarchar(36)) + ''', ' + '''' + CONVERT(Nvarchar(36), Date, 126) + ''', ' + COALESCE(CAST(Pax AS Nvarchar(36)), 'null') + ', ''' + CAST(TableID AS Nvarchar(36)) + ''', ' + '''' + CONVERT(Nvarchar(36), CreationDate, 126) + ''', ' + '''' + CONVERT(Nvarchar(36), LastEditDate, 126) + ''', ' + COALESCE(CAST(BillID AS Nvarchar(36)), 'null') + ', ' + '''' + CAST(TableID AS Nvarchar(36)) + ''', ' + '''' + CAST(CustomerID AS Nvarchar(36)) + ''', ' + COALESCE('''' + REPLACE(Narration, '''', '''''') + '''', 'null') + ', ' + CASE WHEN Void = 1 THEN 'true' ELSE 'false' END + ', ' + COALESCE('''' + REPLACE(VoidReason, '''', '''''') + '''', 'null') + ', ' + CASE WHEN Printed = 0 THEN '0' WHEN Void = 1 THEN '5' ELSE CAST(VoucherType AS Nvarchar(36)) END + ', ' + CAST(KotID AS Nvarchar(36)) + ');' FROM Test.dbo.Vouchers;"
call:copyQuery q-Kots "SELECT 'INSERT INTO kots(id, voucher_id, code, food_table_id, date, user_id) VALUES (''' + CAST(KotID AS Nvarchar(36)) + ''', ' + '''' + CAST(VoucherID AS Nvarchar(36)) + '''' + ', ' + CAST(Code AS Nvarchar(36)) + ', ' + '''' + CAST(TableID AS Nvarchar(36)) + '''' + ', ' + '''' + CONVERT(Nvarchar(36), Date, 126) + ''', ''' + CAST(TableID AS Nvarchar(36)) + ''');' FROM Test.dbo.Kots;"
call:copyQuery r-Inventories "SELECT 'INSERT INTO inventories(id, kot_id, product_id, sort_order, quantity, price, is_happy_hour, tax_rate, tax_id, discount) VALUES (''' + CAST(InventoryID AS Nvarchar(36)) + ''', ' + '''' + CAST(KotID AS Nvarchar(36)) + '''' + ', ' + '''' + CAST(ProductID AS Nvarchar(36)) + '''' + ', ' + CAST(SortOrder AS Nvarchar(36)) + ', ' + CAST(Quantity AS Nvarchar(36)) + ', ' + CAST(Price AS Nvarchar(36)) + ', ' + CASE WHEN IsHappyHour = 1 THEN 'true' ELSE 'false' END + ', ' + CAST(VatRate AS Nvarchar(36)) + ', ' + '''' + CAST(VatID AS Nvarchar(36)) + '''' + ', ' + CAST(Discount AS Nvarchar(36)) + ');' FROM Test.dbo.Inventories;"
call:copyQuery s-InventoryModifiers "SELECT 'INSERT INTO inventory_modifiers(id, inventory_id, modifier_id, price) VALUES (''' + CAST(InventoryModifierID AS Nvarchar(36)) + ''', ' + '''' + CAST(InventoryID AS Nvarchar(36)) + '''' + ', ' + '''' + CAST(ModifierID AS Nvarchar(36)) + '''' + ', 0);' FROM Test.dbo.InventoryModifiers;"
call:copyQuery t-Overview "SELECT 'INSERT INTO overview(id, voucher_id, food_table_id, status) VALUES (''' + CAST(newid() AS Nvarchar(36)) + ''', ' + '''' + CAST(VoucherID AS Nvarchar(36)) + ''', ' + '''' + CAST(FoodTableID AS Nvarchar(36)) + ''', ' + '''' + Status + ''');' FROM Test.dbo.FoodTables WHERE VoucherID IS NOT NULL;"
call:copyQuery u-ProductGroupModifiers "SELECT 'INSERT INTO product_group_modifiers(id, product_group_id, modifier_id, show_automatically) VALUES (''' + CAST(ProductGroupModifierID AS Nvarchar(36)) + ''', ' + COALESCE('''' + CAST(ProductGroupID AS Nvarchar(36)) + '''','null') + ', ' + COALESCE('''' + CAST(ModifierID AS Nvarchar(36)) + '''','null') + ', ' + CASE WHEN ShowAutomatically = 1 THEN 'true' ELSE 'false' END + ');' FROM Test.dbo.ProductGroupModifiers;"
call:copyQuery v-Reprints "SELECT 'INSERT INTO reprints(id, user_id, date, voucher_id) VALUES (''' + CAST(ReprintID AS Nvarchar(36)) + ''', ' + '''' + CAST(UserID AS Nvarchar(36)) + '''' + ', ' + '''' + CONVERT(Nvarchar(36), Date, 126) + '''' + ', ' + '''' + CAST(VoucherID AS Nvarchar(36)) + '''' + ');' FROM Test.dbo.Reprints;"
call:copyQuery w-Settings "SELECT 'INSERT INTO public.settings(id, name, data) VALUES (''' + CAST(SettingID AS Nvarchar(36)) + ''', ' + '''' + REPLACE(Name, '''', '''''') + '''' + ', ' + '''' + REPLACE(Details, '''', '''''') + '''' + ');' FROM Test.dbo.Settings;"
call:copyQuery x-VoucherSettlements "SELECT 'INSERT INTO settlements(id, voucher_id, settled, amount) VALUES (''' + CAST(VoucherSettlementID AS Nvarchar(36)) + ''', ' + '''' + CAST(VoucherID AS Nvarchar(36)) + '''' + ', ' + CAST(Settled AS Nvarchar(36)) + ', ' + CAST(Amount AS Nvarchar(36)) + ');' FROM Test.dbo.VoucherSettlements;"
call:copyQuery a-Roles "SELECT GroupID, Name FROM Test.dbo.Auth_Groups;"
call:copyQuery b-Permissions "SELECT RoleID, Name FROM Test.dbo.Auth_Roles;"
call:copyQuery c-RolePermissions "SELECT RoleGroupID, RoleID, GroupID FROM Test.dbo.Auth_RoleGroups;"
call:copyQuery d-Users "SELECT UserID, Name, Password, CASE WHEN LockedOut = 1 THEN 't' ELSE 'f' END FROM Test.dbo.Auth_Users;"
call:copyQuery e-UserRoles "SELECT UserGroupID, UserID, GroupID FROM Test.dbo.Auth_UserGroups;"
call:copyQuery f-Customers "SELECT CustomerID, Name, CASE WHEN Address = '' THEN '\N' ELSE Address END, Phone + ' ' + Name FROM Test.dbo.Customers WHERE CustomerID != '2c716f4b-0736-429a-ad51-610d7c47cb5e';"
call:copyQuery g-FoodTables "SELECT FoodTableID, Name, 0, CASE WHEN IsActive = 1 THEN 't' ELSE 'f' END, '3f13f6e7-dc76-4fca-8fdb-b2bbf29b35df', SortOrder FROM Test.dbo.FoodTables;"
call:copyQuery h-Taxes "SELECT TaxID, Name, CASE WHEN Rate = .025 THEN .05 ELSE Rate END, 'f' FROM Test.dbo.Taxes;"
call:copyQuery i-MenuCategories "SELECT ProductGroupID, Name, CASE WHEN IsActive = 1 THEN 't' ELSE 'f' END, 'f', SortOrder FROM Test.dbo.ProductGroups;"
call:copyQuery j-SaleCategories "SELECT newid(), pgo.GroupType, 1, CAST((select top 1 t.TaxID from Test.dbo.Taxes t inner join Test.dbo.Products p on t.TaxID = p.VatID inner join Test.dbo.ProductGroups pg on p.ProductGroupID = pg.ProductGroupID where pg.GroupType = pgo.GroupType) AS Nvarchar(36)) FROM (select distinct pgx.GroupType from Test.dbo.ProductGroups pgx) as pgo;"
call:copyQuery k-Products "SELECT ProductID, Name, COALESCE(Units, '\N'), ProductGroupID, (SELECT GroupType FROM Test.dbo.ProductGroups pgs WHERE pgs.ProductGroupID = Products.ProductGroupID), Price, CASE WHEN HasHappyHour = 1 THEN 't' ELSE 'f' END , CASE WHEN IsActive = 1 THEN 't' ELSE 'f' END, CASE WHEN IsNotAvailable = 1 THEN 't' ELSE 'f' END, SortOrder, Quantity FROM Test.dbo.Products;"
call:copyQuery l-Modifiers "SELECT ModifierID, Name, CASE WHEN ShowInBill = 1 THEN 't' ELSE 'f' END, 't', 'e046ad33-dc65-4c78-8833-c3d3538d44c0', Price FROM Test.dbo.Modifiers;"
call:copyQuery m-Sections "SELECT NewID(), Location, 'f' FROM Test.dbo.PrintLocations GROUP BY Location;"
call:copyQuery n-Printers "SELECT NewID(), Printer, Printer, 0x1d564103 FROM Test.dbo.PrintLocations GROUP BY Printer, CutCode;"
call:copyQuery o-SectionPrinters "SELECT PrintLocationID, COALESCE(CAST(ProductGroupID AS Nvarchar(36)), '\N'), Location, Printer, Copies FROM Test.dbo.PrintLocations;"
call:copyQuery p-Vouchers "SELECT VoucherID, DATEADD(minute, -330, Date), Pax, UserID, DATEADD(minute, -330, CreationDate), DATEADD(minute, -330, LastEditDate), COALESCE(CAST(BillID AS Nvarchar(36)), '\N'), TableID, CustomerID, COALESCE(Narration, '\N'), VoidReason, CASE WHEN Printed = 0 THEN '0' WHEN Void = 1 THEN '5' ELSE VoucherType END, KotID FROM Test.dbo.Vouchers;"
call:copyQuery q-Kots "SELECT KotID, VoucherID, Code, TableID, DATEADD(minute, -330, Date), UserID FROM Test.dbo.Kots;"
call:copyQuery r-Inventories "SELECT InventoryID, KotID, ProductID, SortOrder, Quantity, Price, CASE WHEN IsHappyHour = 1 THEN 't' ELSE 'f' END, CASE WHEN VatRate = .025 THEN .05 ELSE VatRate END, VatID, Discount FROM Test.dbo.Inventories;"
call:copyQuery s-InventoryModifiers "SELECT InventoryModifierID, InventoryID, ModifierID, 0 FROM Test.dbo.InventoryModifiers;"
call:copyQuery t-Overview "SELECT newid(), VoucherID, FoodTableID, Status FROM Test.dbo.FoodTables WHERE VoucherID IS NOT NULL;"
call:copyQuery u-Reprints "SELECT ReprintID, UserID, DATEADD(minute, -330, Date), VoucherID FROM Test.dbo.Reprints;"
call:copyQuery v-Settings "SELECT SettingID, Name, '{""Text"": ""' + Details + '""}' FROM Test.dbo.Settings;"
call:copyQuery w-VoucherSettlements "SELECT VoucherSettlementID, VoucherID, Settled, Amount FROM Test.dbo.VoucherSettlements;"
goto:eof
:copyQuery - here starts my function identified by it's label
echo.
echo. Copying %~1
bcp "%~2" queryout Data\%~1.sql -c -t',' -S . -U sa -P 123456
bcp "%~2" queryout csv\%~1.csv -c -t\t -S . -U sa -P 123456
goto:eof
+87
View File
@@ -0,0 +1,87 @@
FROM node:lts-trixie-slim AS base
# Install dependencies only when needed
FROM base AS deps
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 make g++ \
&& rm -rf /var/lib/apt/lists/*
# Install dependencies based on the preferred package manager
COPY bookie/package.json bookie/yarn.lock* bookie/package-lock.json* bookie/pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY /bookie ./
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
FROM python:3.14-slim AS runner
LABEL maintainer="Amritanshu <docker@tanshu.com>"
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
# Install uv
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#installing-uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Compile bytecode
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#compiling-bytecode
ENV UV_COMPILE_BYTECODE=1
# Disable development dependencies
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#installing-a-project
ENV UV_NO_DEV=1
# uv Cache
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#caching
ENV UV_LINK_MODE=copy
WORKDIR /app/
# Place executables in the environment at the front of the path
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#using-the-environment
ENV PATH="/app/.venv/bin:$PATH"
# Install dependencies
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=barker/uv.lock,target=uv.lock \
--mount=type=bind,source=barker/pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project
COPY /barker ./
COPY --from=builder /frontend/browser /app/frontend
# Sync the project
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked
ENV PYTHONPATH=/app
EXPOSE 80
RUN chmod +x /app/docker-entrypoint.sh \
&& ln -s /app/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
# at the end of your Dockerfile, before CMD or after EXPOSE
# Kill the main process if the healthcheck fails. This will kill the container and restart policy can restart it.
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl -fsS http://localhost/health || kill -s 15 1
CMD ["gunicorn", "barker.main:app", "--worker-class", "uvicorn.workers.UvicornWorker", "--config", "./gunicorn.conf.py", "--log-config", "./logging.conf"]
-2
View File
@@ -1,2 +0,0 @@
include *.txt *.ini *.cfg *.rst
recursive-include barker
+28
View File
@@ -0,0 +1,28 @@
.PHONY: build-production
build-production: ## Build the production docker image.
@docker buildx build \
--platform linux/amd64,linux/arm64/v8 \
--tag registry.tanshu.com/barker:latest \
$(if $(TAG),--tag registry.tanshu.com/barker:$(TAG)) \
--pull \
--push \
git@git.tanshu.com:tanshu/barker.git
.PHONY: build-check
build-check: ## Multi-arch build without push (compile check)
@docker buildx build \
--platform linux/amd64,linux/arm64/v8 \
--tag barker:test \
--pull \
--progress=plain \
git@git.tanshu.com:tanshu/barker.git
.PHONY: build-check-local
build-check-local: ## Multi-arch build without push (compile check)
@git archive --format=tar HEAD | docker buildx build \
--platform linux/amd64 \
--tag barker:test \
--pull \
--progress=plain \
--load \
-
+119
View File
@@ -0,0 +1,119 @@
# Installation linux
This project uses **uv** for Python version, dependency and virtual environment management.
---
## 1. Install uv
### 1.1 Install uv (if not already installed)
```zsh
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Reload the shell:
```zsh
exec "$SHELL"
```
Verify:
```zsh
uv --version
```
## 2. Install Python 3.14 using uv
```zsh
uv python install 3.14
```
Set the local Python version for this project:
```zsh
uv python pin 3.14
```
Verify:
```zsh
python --version
```
---
## 3. Install project dependencies
### 3.1 Install dependencies
```zsh
uv sync
```
This will:
* Create a local `.venv`
* Install all dependencies defined in `pyproject.toml`
* Generate/update `uv.lock`
* Use Python **3.14**
---
## 4. Activate the environment (optional)
```zsh
source .venv/bin/activate
```
Deactivate:
```zsh
deactivate
```
Or run commands directly:
```zsh
uv run python your_script.py
```
---
## 5. Install nvm
### 5.1 Install nvm (if not already installed)
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
```
### 5.2 Configure shell for pyenv
Add the following to `~/.zshrc`:
```zsh
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
```
Reload the shell:
```zsh
exec "$SHELL"
```
Verify:
```zsh
nvm --version
```
---
## Notes
* Do **not** use system-wide pip for this project.
* Do **not** install dependencies manually.
* Always use `uv sync` to install or update dependencies.
* Commit both: `pyproject.toml` and `uv.lock`
* PostgreSQL must be running and properly configured before application startup.
-41
View File
@@ -1,41 +0,0 @@
# Installation on Ubuntu 14.04
## Prepare the system
1. Install system-wide pip
**Currently the ensurepip is broken in Python 3.4 / Ubuntu 14.04, therefore we need to install system-wide pip**
```bash
sudo apt-get install python3-pip
```
2. Install postgresql
```bash
sudo apt-get install postgresql postgresql-client pgadmin3
```
[Full Guide](https://help.ubuntu.com/community/PostgreSQL)
3. Configure the server and set password
```bash
sudo -u postgres psql postgres
\password postgres
```
4. Install components to build psycopg2
```bash
sudo apt-get install libpq-dev python3-dev
```
## Setup the virutal environment
1. Create the virtual environment
**Currently the ensurepip is broken in Python 3.4 / Ubuntu 14.04, therefore --without-pip is needed.**
```bash
pyvenv-3.4 env --without-pip --system-site-packages
```
2. Activate the virtual environment
```bash
source env/bin/activate
```
3. Install pyramid, sqlachemy, psycopg2 and other dependencies:
```bash
python3 -m pip install pyramid waitress sqlalchemy zope.sqlalchemy psycopg2
```
+2
View File
@@ -0,0 +1,2 @@
app/package.json
app/pyproject.toml
+19
View File
@@ -0,0 +1,19 @@
HOST=0.0.0.0
PORT=80
LOG_LEVEL=WARN
DEBUG=false
SQLALCHEMY_DATABASE_URI=postgresql+psycopg://postgres:123456@postgres:5432/petty{{ app_name }}
MODULE_NAME=barker.main
PROJECT_NAME=barker
MAX_WORKERS=4
NATS_URL={{ nats_url }}
SUBJECT=barker.print
STREAM=barker_print_stream
SECRET_KEY={{ secret_key }}
MIDDLEWARE_SECRET_KEY={{ middleware_key }}
ALGORITHM=HS256
JWT_TOKEN_EXPIRE_MINUTES=30
NEW_DAY_OFFSET_MINUTES=420
TIMEZONE_OFFSET_MINUTES=330
ALEMBIC_LOG_LEVEL=INFO
ALEMBIC_SQLALCHEMY_LOG_LEVEL=WARN
+3
View File
@@ -0,0 +1,3 @@
{{ host }} {
reverse_proxy {{ host_directory }}:80
}
+14
View File
@@ -0,0 +1,14 @@
# - Comments begin with the '#' character
# - Blank lines are ignored
# - Groups of hosts are delimited by [header] elements
# - You can enter hostnames or ip addresses
# - A hostname/ip can be a member of multiple groups
[barker]
pkl ansible_host=clair var_file=vars/pkl.yml
chd ansible_host=clair var_file=vars/chd.yml
mhl ansible_host=clair var_file=vars/mhl.yml
hin ansible_host=clair var_file=vars/hin.yml
[all:vars]
ansible_python_interpreter=/usr/bin/python3
+20
View File
@@ -0,0 +1,20 @@
#################################################
# DO Community Playbooks: Docker
#################################################
---
- name: Deploy Barker
hosts: barker
become: true
vars_files:
- vars/default.yml
pre_tasks:
- name: Load per-host vars file from inventory (var_file)
ansible.builtin.include_vars:
file: "{{ var_file }}"
when: var_file is defined
roles:
- network
- barker
- caddy
+3
View File
@@ -0,0 +1,3 @@
---
collections:
- name: community.docker
+36
View File
@@ -0,0 +1,36 @@
---
# Tasks for docker role
- name: Log in to Docker registry
community.docker.docker_login:
registry: "{{ registry }}"
username: "{{ username }}"
password: "{{ password }}"
- name: Pull Barker image
community.docker.docker_image:
name: "{{ image_name }}"
source: pull
state: present
force_source: true
- name: Ensure Host Directory exists
ansible.builtin.file:
path: "/var/lib/{{ host_directory }}"
state: directory
mode: "0755"
- name: Upload the .env file
ansible.builtin.template:
src: "files/.env"
dest: "/var/lib/{{ host_directory }}/.env"
mode: "0644"
- name: Create barker container
community.docker.docker_container:
name: "{{ host_directory }}"
image: "{{ image_name }}"
state: started
restart_policy: "unless-stopped"
env_file: "/var/lib/{{ host_directory }}/.env"
networks:
- name: "{{ docker_network }}"
+7
View File
@@ -0,0 +1,7 @@
---
caddy_container: caddy
caddy_caddyfile_path: /var/lib/caddy/conf/Caddyfile
# snippet_block: |
# {{ host }} {
# reverse_proxy {{ barker_container }}:80
# }
+5
View File
@@ -0,0 +1,5 @@
---
- name: Reload Caddy configuration
ansible.builtin.command: "docker exec -w /etc/caddy {{ caddy_container }} caddy reload"
listen: "Reload Caddy"
changed_when: true
+27
View File
@@ -0,0 +1,27 @@
---
- name: Read snippet from template file
ansible.builtin.set_fact:
caddy_snippet_block: "{{ lookup('template', 'files/Caddyfile.j2') }}"
- name: Read current Caddyfile
ansible.builtin.slurp:
path: "{{ caddy_caddyfile_path }}"
register: caddy_caddyfile_raw
- name: Decode Caddyfile content
ansible.builtin.set_fact:
caddy_caddyfile_content: "{{ caddy_caddyfile_raw['content'] | b64decode }}"
- name: Check if snippet already exists
ansible.builtin.set_fact:
caddy_snippet_present: "{{ caddy_snippet_block in caddy_caddyfile_content }}"
- name: Add snippet if missing
ansible.builtin.blockinfile:
path: "{{ caddy_caddyfile_path }}"
marker: "# {mark} Ansible managed Caddy snippet for {{ host }}"
block: "{{ caddy_snippet_block }}"
create: true
mode: "0644"
when: not caddy_snippet_present
notify: Reload Caddy configuration
+10
View File
@@ -0,0 +1,10 @@
---
- name: Ensure 'barker' Docker network exists
community.docker.docker_network:
name: "{{ docker_network }}"
state: present
connected:
- caddy
- valkey
- postgres
appends: true
+8
View File
@@ -0,0 +1,8 @@
---
app_name: chd
secret_key: d9e4facec94d7e0bf3d63ca03b1d78d834b158627b6593274f7fe27f6aed6db4
middleware_key: 8d5f28b083
host: knox.greatbear.in
host_directory: "barker-{{ app_name }}"
docker_network: "{{ host_directory }}_net"
+16
View File
@@ -0,0 +1,16 @@
---
# name: xxx
# # openssl rand -hex 32
# secret_key: 0000000000000000000000000000000000000000000000000000000000000000
# # openssl rand -hex 5
# middleware_key: 0000000000
registry: registry.tanshu.com
username: ta-registry
password: ff28a01f00c0f39315d94cd9dcb1e554968dba25676a8ea5f2be34e96a9a099f
title: barker
tag: latest
image_name: "{{ registry }}/barker:{{ tag }}"
# host_directory: "barker-{{ name }}"
+9
View File
@@ -0,0 +1,9 @@
---
app_name: hin
secret_key: 029858927beda45d76317729824ae500d0938f04d4ca955f3afa95061c9a9298
middleware_key: c4b0b98ff9
host: knox.mozimo.in
host_directory: "barker-{{ app_name }}"
docker_network: "{{ host_directory }}_net"
nats_url: "nats://clair.lan.tanshu.com:4222"
+8
View File
@@ -0,0 +1,8 @@
---
app_name: mhl
secret_key: 8b7f704ad1bbee3caa683a57440a6a371937fe5f64a7712a6a15bf3165c3c598
middleware_key: 1aa5487223
host: knox.hngmohali.com
host_directory: "barker-{{ app_name }}"
docker_network: "{{ host_directory }}_net"
+8
View File
@@ -0,0 +1,8 @@
---
app_name: pkl
secret_key: bd6e5dee0f3b8a6f0db50f7aa08e91d55b2ae5ab6df126defa37e80602481002
middleware_key: 1d34ef6597
host: knox.hopsngrains.com
host_directory: "barker-{{ app_name }}"
docker_network: "{{ host_directory }}_net"
+3
View File
@@ -0,0 +1,3 @@
*.pyc
*/__pycache__/
*.egg-info/
+23
View File
@@ -0,0 +1,23 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.16.2
hooks:
# Run the linter.
- id: ruff
args:
- --fix
- --config=barker/pyproject.toml
# Run the formatter.
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: debug-statements
+22
View File
@@ -0,0 +1,22 @@
{
"python.defaultInterpreterPath": "/home/tanshu/programming/barker/barker/.venv/bin/python",
"python.interpreterPath": "/home/tanshu/programming/barker/barker/.venv/bin/python",
"python.venvFolders": [
".venv"
],
"python.terminal.activateEnvironment": true,
"python.analysis.typeCheckingMode": "basic",
"python.analysis.extraPaths": [
"${workspaceFolder}"
],
"python.autoComplete.extraPaths": [
"${workspaceFolder}"
],
"editor.formatOnSave": true,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff"
},
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "always"
}
}
-36
View File
@@ -1,36 +0,0 @@
import os
from pyramid.config import Configurator
from pyramid.session import SignedCookieSessionFactory
from barker.renderers import json_renderer, CSVRenderer
from barker.transactional_view_deriver import transactional_view
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
if 'sqlalchemy.url' not in settings:
DB_NAME = os.environ['DB_NAME']
DB_USER = os.environ['DB_USER']
DB_PASS = os.environ['DB_PASS']
DB_URI = 'postgresql://{0}:{1}@postgres:5432/{2}'.format(
DB_USER, DB_PASS, DB_NAME
)
settings['sqlalchemy.url'] = DB_URI
SECRET_KEY = os.environ.get('SECRET_KEY', settings.get('secret_key', ''))
session_factory = SignedCookieSessionFactory(SECRET_KEY, timeout=None)
config = Configurator(
settings=settings,
session_factory=session_factory
)
config.include('.security')
config.include('.models')
config.include('.routes')
config.add_view_deriver(transactional_view)
config.add_renderer(name='json', factory=json_renderer)
config.add_renderer(name='csv', factory=CSVRenderer)
config.scan()
return config.make_wsgi_app()
+85
View File
@@ -0,0 +1,85 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = driver://user:pass@localhost/dbname
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks=black
# black.type=console_scripts
# black.entrypoint=black
# black.options=-l 79
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
+1
View File
@@ -0,0 +1 @@
Generic single-database configuration.
+73
View File
@@ -0,0 +1,73 @@
import logging
from sqlalchemy import create_engine, pool
from alembic import context
from barker.core.config import settings
from barker.db.base import reg
logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(settings.ALEMBIC_LOG_LEVEL)
logging.getLogger("alembic").setLevel(settings.ALEMBIC_LOG_LEVEL)
# Interpret the config file for Python logging.
# This line sets up loggers basically.
target_metadata = reg.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = settings.SQLALCHEMY_DATABASE_URI
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
compare_type=True,
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = create_engine(
settings.SQLALCHEMY_DATABASE_URI,
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata, compare_type=True)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
+24
View File
@@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}
@@ -0,0 +1,48 @@
"""temporal products fixed
Revision ID: 0e326930b8a4
Revises: 3609c44430c8
Create Date: 2021-10-22 09:36:11.746119
"""
import sqlalchemy as sa
from sqlalchemy import column, func, table, text
from sqlalchemy.dialects import postgresql
from alembic import op
# revision identifiers, used by Alembic.
revision = "0e326930b8a4"
down_revision = "3609c44430c8"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_unique_constraint(op.f("uq_vouchers_bill_id"), "vouchers", ["bill_id", "voucher_type"])
prod = table(
"product_versions",
column("id", postgresql.UUID(as_uuid=True)),
column("product_id", postgresql.UUID(as_uuid=True)),
column("valid_from", sa.Date()),
column("valid_till", sa.Date()),
)
op.create_exclude_constraint(
op.f("uq_product_versions_product_id"),
"product_versions",
(prod.c.product_id, "="),
(func.daterange(prod.c.valid_from, prod.c.valid_till, text("'[]'")), "&&"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f("uq_vouchers_bill_id"), "vouchers", type_="unique")
# ### end Alembic commands ###
@@ -0,0 +1,129 @@
"""Cancel Bill
Revision ID: 1d95eb10ea20
Revises: 5967e6603c3e
Create Date: 2023-03-17 01:04:25.809115
"""
import sqlalchemy as sa
from sqlalchemy import column, table
from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects.postgresql import ENUM
from alembic import op
# revision identifiers, used by Alembic.
revision = "1d95eb10ea20"
down_revision = "5967e6603c3e"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# op.execute("ALTER TYPE voucher_type ADD VALUE 'CANCEL';")
reporting_level = sa.Enum("Skip", "Aggregate", "Detailed", name="reportinglevel")
reporting_level.create(op.get_bind())
voucher_type = sa.Enum("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="vouchertype")
voucher_type.create(op.get_bind())
op.execute("ALTER TABLE settle_options ALTER COLUMN voucher_type DROP DEFAULT;")
op.execute("ALTER TABLE settle_options ALTER COLUMN reporting_level DROP DEFAULT;")
op.execute(
"ALTER TABLE settle_options ALTER COLUMN voucher_type TYPE vouchertype USING voucher_type::text::vouchertype;"
)
op.execute(
"ALTER TABLE settle_options "
+ "ALTER COLUMN reporting_level "
+ "TYPE reportinglevel USING reporting_level::text::reportinglevel;"
)
op.execute("ALTER TABLE vouchers ALTER COLUMN voucher_type DROP DEFAULT;")
op.execute("ALTER TABLE vouchers ALTER COLUMN voucher_type TYPE vouchertype USING voucher_type::text::vouchertype;")
op.execute("DROP TYPE voucher_type;")
op.execute("DROP TYPE reporting_level;")
op.create_index(op.f("ix_vouchers_date"), "vouchers", ["date"], unique=False)
settle_options()
regimes()
# ### end Alembic commands ###
def settle_options():
reporting_level = ENUM("Skip", "Aggregate", "Detailed", name="reporting_level", create_type=False)
voucher_type = ENUM(
"KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="voucher_type", create_type=False
)
so = table(
"settle_options",
column("id", sa.Integer()),
column("name", sa.Unicode(length=255)),
column("has_reason", sa.Boolean()),
column("reporting_level", reporting_level),
column("voucher_type", voucher_type),
column("is_fixture", sa.Boolean()),
)
op.execute(
so.insert().values(
id=12,
name="Cancelled",
has_reason=True,
reporting_level="Detailed",
voucher_type="CANCEL",
is_fixture=True,
)
)
def regimes():
re = sa.table(
"regimes",
sa.column("id", sa.Integer()),
sa.column("name", sa.Unicode(length=255)),
sa.column("header", sa.Unicode(length=255)),
sa.column("prefix", sa.Unicode(length=255)),
sa.column("is_fixture", sa.Boolean()),
)
op.execute(
re.insert().values(
id=12,
name="Cancelled",
header="",
prefix="C",
is_fixture=True,
)
)
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_vouchers_date"), table_name="vouchers")
op.alter_column(
"vouchers",
"voucher_type",
existing_type=sa.Enum("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="vouchertype"),
type_=postgresql.ENUM("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", name="voucher_type"),
existing_nullable=False,
existing_server_default=sa.text("'KOT'::voucher_type"),
)
op.alter_column(
"settle_options",
"reporting_level",
existing_type=sa.Enum("Skip", "Aggregate", "Detailed", name="reportinglevel"),
type_=postgresql.ENUM("Skip", "Aggregate", "Detailed", name="reporting_level"),
existing_nullable=False,
existing_server_default=sa.text("'Skip'::reporting_level"),
)
op.alter_column(
"settle_options",
"voucher_type",
existing_type=sa.Enum("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="vouchertype"),
type_=postgresql.ENUM("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", name="voucher_type"),
existing_nullable=False,
existing_server_default=sa.text("'KOT'::voucher_type"),
)
# ### end Alembic commands ###
@@ -0,0 +1,291 @@
"""combine skus
Revision ID: 1e0daf6bc1ae
Revises: c116517977af
Create Date: 2026-01-23 15:56:01.354574
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "1e0daf6bc1ae"
down_revision = "c116517977af"
branch_labels = None
depends_on = None
def upgrade():
op.execute(
sa.text(
"""
-- Merge contiguous identical sku_versions (same business fields) into single rows.
-- 0) Work table: compute vf/vt sentinels + run_id (islands)
CREATE TEMP TABLE tmp_sku_work ON COMMIT DROP AS
WITH base AS (
SELECT
sv.id,
sv.sku_id,
sv.units,
sv.fraction,
sv.product_yield,
sv.cost_price,
sv.sale_price,
sv.has_happy_hour,
sv.menu_category_id,
sv.valid_from,
sv.valid_till,
COALESCE(sv.valid_from, DATE '1900-01-01') AS vf,
COALESCE(sv.valid_till, DATE '9999-12-31') AS vt
FROM sku_versions sv
),
ordered AS (
SELECT
b.*,
LAG(vt) OVER (
PARTITION BY sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id
ORDER BY vf, vt, id
) AS prev_vt
FROM base b
),
flagged AS (
SELECT
o.*,
CASE
WHEN o.prev_vt IS NULL THEN 1
WHEN o.vf = (o.prev_vt + INTERVAL '1 day')::date THEN 0
ELSE 1
END AS is_new_run
FROM ordered o
),
runs AS (
SELECT
f.*,
SUM(is_new_run) OVER (
PARTITION BY sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id
ORDER BY vf, vt, id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS run_id
FROM flagged f
)
SELECT
id,
sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id,
valid_from, valid_till,
vf, vt,
run_id
FROM runs;
-- 1) Survivors + merged validity (only runs with >1 row)
CREATE TEMP TABLE tmp_sku_merge ON COMMIT DROP AS
WITH run_stats AS (
SELECT
sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id,
run_id,
MIN(vf) AS min_vf,
MAX(vt) AS max_vt,
COUNT(*) AS cnt
FROM tmp_sku_work
GROUP BY
sku_id, units, fraction, product_yield, cost_price, sale_price, has_happy_hour, menu_category_id, run_id
HAVING COUNT(*) > 1
),
survivors AS (
SELECT DISTINCT ON (
w.sku_id, w.units, w.fraction, w.product_yield, w.cost_price, w.sale_price, w.has_happy_hour, w.menu_category_id, w.run_id
)
w.sku_id, w.units, w.fraction, w.product_yield, w.cost_price, w.sale_price, w.has_happy_hour, w.menu_category_id, w.run_id,
w.id AS survivor_id
FROM tmp_sku_work w
JOIN run_stats s
ON s.sku_id = w.sku_id
AND s.units = w.units
AND s.fraction = w.fraction
AND s.product_yield = w.product_yield
AND s.cost_price = w.cost_price
AND s.sale_price = w.sale_price
AND s.has_happy_hour = w.has_happy_hour
AND s.menu_category_id = w.menu_category_id
AND s.run_id = w.run_id
ORDER BY
w.sku_id, w.units, w.fraction, w.product_yield, w.cost_price, w.sale_price, w.has_happy_hour, w.menu_category_id, w.run_id,
w.vf, w.vt, w.id
)
SELECT
sv.survivor_id,
sv.sku_id, sv.units, sv.fraction, sv.product_yield, sv.cost_price, sv.sale_price, sv.has_happy_hour, sv.menu_category_id, sv.run_id,
CASE WHEN s.min_vf = DATE '1900-01-01' THEN NULL ELSE s.min_vf END AS new_valid_from,
CASE WHEN s.max_vt = DATE '9999-12-31' THEN NULL ELSE s.max_vt END AS new_valid_till
FROM survivors sv
JOIN run_stats s
ON s.sku_id = sv.sku_id
AND s.units = sv.units
AND s.fraction = sv.fraction
AND s.product_yield = sv.product_yield
AND s.cost_price = sv.cost_price
AND s.sale_price = sv.sale_price
AND s.has_happy_hour = sv.has_happy_hour
AND s.menu_category_id = sv.menu_category_id
AND s.run_id = sv.run_id;
-- 2) Rows to delete = everything in a merged run except the survivor
CREATE TEMP TABLE tmp_sku_delete ON COMMIT DROP AS
SELECT w.id
FROM tmp_sku_work w
JOIN tmp_sku_merge m
ON m.sku_id = w.sku_id
AND m.units = w.units
AND m.fraction = w.fraction
AND m.product_yield = w.product_yield
AND m.cost_price = w.cost_price
AND m.sale_price = w.sale_price
AND m.has_happy_hour = w.has_happy_hour
AND m.menu_category_id = w.menu_category_id
AND m.run_id = w.run_id
WHERE w.id <> m.survivor_id;
-- 3) Delete first (avoid exclusion overlap), then update survivor validity
DELETE FROM sku_versions
WHERE id IN (SELECT id FROM tmp_sku_delete);
UPDATE sku_versions sv
SET valid_from = m.new_valid_from,
valid_till = m.new_valid_till
FROM tmp_sku_merge m
WHERE sv.id = m.survivor_id;
"""
)
)
op.execute(
sa.text(
"""
-- Merge contiguous identical product_versions (ignoring fraction_units)
-- 0) Work table: compute vf/vt sentinels + run_id (islands)
CREATE TEMP TABLE tmp_pv_work ON COMMIT DROP AS
WITH base AS (
SELECT
pv.id,
pv.product_id,
pv.name,
pv.valid_from,
pv.valid_till,
COALESCE(pv.valid_from, DATE '1900-01-01') AS vf,
COALESCE(pv.valid_till, DATE '9999-12-31') AS vt
FROM product_versions pv
),
ordered AS (
SELECT
b.*,
LAG(vt) OVER (
PARTITION BY product_id, name
ORDER BY vf, vt, id
) AS prev_vt
FROM base b
),
flagged AS (
SELECT
o.*,
CASE
WHEN o.prev_vt IS NULL THEN 1
WHEN o.vf = (o.prev_vt + INTERVAL '1 day')::date THEN 0
ELSE 1
END AS is_new_run
FROM ordered o
),
runs AS (
SELECT
f.*,
SUM(is_new_run) OVER (
PARTITION BY product_id, name
ORDER BY vf, vt, id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS run_id
FROM flagged f
)
SELECT
id,
product_id,
name,
valid_from,
valid_till,
vf, vt,
run_id
FROM runs;
-- 1) Survivors + merged validity (only runs with >1 row)
CREATE TEMP TABLE tmp_pv_merge ON COMMIT DROP AS
WITH run_stats AS (
SELECT
product_id,
name,
run_id,
MIN(vf) AS min_vf,
MAX(vt) AS max_vt,
COUNT(*) AS cnt
FROM tmp_pv_work
GROUP BY product_id, name, run_id
HAVING COUNT(*) > 1
),
survivors AS (
-- deterministic survivor: earliest vf, then earliest vt, then lowest id
SELECT DISTINCT ON (w.product_id, w.name, w.run_id)
w.product_id,
w.name,
w.run_id,
w.id AS survivor_id
FROM tmp_pv_work w
JOIN run_stats s
ON s.product_id = w.product_id
AND s.name = w.name
AND s.run_id = w.run_id
ORDER BY w.product_id, w.name, w.run_id, w.vf, w.vt, w.id
)
SELECT
sv.survivor_id,
sv.product_id,
sv.name,
sv.run_id,
CASE WHEN s.min_vf = DATE '1900-01-01' THEN NULL ELSE s.min_vf END AS new_valid_from,
CASE WHEN s.max_vt = DATE '9999-12-31' THEN NULL ELSE s.max_vt END AS new_valid_till
FROM survivors sv
JOIN run_stats s
ON s.product_id = sv.product_id
AND s.name = sv.name
AND s.run_id = sv.run_id;
-- 2) Rows to delete = everything in a merged run except the survivor
CREATE TEMP TABLE tmp_pv_delete ON COMMIT DROP AS
SELECT w.id
FROM tmp_pv_work w
JOIN tmp_pv_merge m
ON m.product_id = w.product_id
AND m.name = w.name
AND m.run_id = w.run_id
WHERE w.id <> m.survivor_id;
-- 3) Delete first (avoid exclusion overlap), then update survivor validity
DELETE FROM product_versions
WHERE id IN (SELECT id FROM tmp_pv_delete);
UPDATE product_versions pv
SET valid_from = m.new_valid_from,
valid_till = m.new_valid_till
FROM tmp_pv_merge m
WHERE pv.id = m.survivor_id;
"""
)
)
# Compatibility updates for GuestBookType enum
op.execute("ALTER TYPE guestbooktype ADD VALUE IF NOT EXISTS 'running'")
op.execute("ALTER TYPE guestbooktype ADD VALUE IF NOT EXISTS 'printed'")
op.execute("ALTER TYPE guestbooktype ADD VALUE IF NOT EXISTS 'old'")
def downgrade():
pass
@@ -0,0 +1,236 @@
"""add stock_keeping_units + sku_versions"""
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from alembic import op
# revision identifiers, used by Alembic.
revision = "32c508eed4df"
down_revision = "7e9944b430d6"
branch_labels = None
depends_on = None
def upgrade():
# ---------------------------------------------------------------------
# 0) Add NON-VERSIONED sort_order columns (but do NOT remove versioned ones here)
# ---------------------------------------------------------------------
op.add_column(
"products",
sa.Column("sort_order", sa.Integer(), nullable=False, server_default=sa.text("0")),
)
# ---------------------------------------------------------------------
# 1) stock_keeping_units
# ---------------------------------------------------------------------
op.create_table(
"stock_keeping_units",
sa.Column("id", sa.Uuid(), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("product_id", sa.Uuid(), nullable=False),
sa.Column("is_not_available", sa.Boolean(), nullable=False, server_default=sa.text("false")),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["product_id"], ["products.id"], name=op.f("fk_stock_keeping_units_product_id_products")
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_stock_keeping_units")),
)
# ---------------------------------------------------------------------
# 2) sku_versions
# ---------------------------------------------------------------------
op.create_table(
"sku_versions",
sa.Column("id", sa.Uuid(), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("sku_id", sa.Uuid(), nullable=False),
sa.Column("units", sa.Unicode(), nullable=False),
sa.Column("fraction", sa.Numeric(precision=15, scale=5), nullable=False),
sa.Column("product_yield", sa.Numeric(precision=15, scale=5), nullable=False),
sa.Column("cost_price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("sale_price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("has_happy_hour", sa.Boolean(), nullable=False),
sa.Column("menu_category_id", sa.Uuid(), nullable=False),
sa.Column("valid_from", sa.Date(), nullable=True),
sa.Column("valid_till", sa.Date(), nullable=True),
postgresql.ExcludeConstraint(
(sa.column("sku_id"), "="),
(sa.text("daterange(valid_from, valid_till, '[]')"), "&&"),
using="gist",
name=op.f("uq_sku_versions_sku_id"),
),
postgresql.ExcludeConstraint(
(sa.column("sku_id"), "="),
(sa.column("units"), "="),
(sa.text("daterange(valid_from, valid_till, '[]')"), "&&"),
using="gist",
name=op.f("uq_sku_versions_sku_id_units"),
),
# postgresql.ExcludeConstraint((sa.column('units'), '='), (sa.text("daterange(valid_from, valid_till, '[]')"), '&&'), using='gist', name=op.f('uq_sku_versions_units')),
sa.ForeignKeyConstraint(
["menu_category_id"], ["menu_categories.id"], name=op.f("fk_sku_versions_menu_category_id_menu_categories")
),
sa.ForeignKeyConstraint(
["sku_id"], ["stock_keeping_units.id"], name=op.f("fk_sku_versions_sku_id_stock_keeping_units")
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_sku_versions")),
sa.CheckConstraint("length(trim(units)) >= 1", name="units_not_blank"),
)
# ---------------------------------------------------------------------
# 3) Data migration
# - Create exactly 1 SKU per product.
# - Migrate existing product_versions rows into sku_versions rows.
# ---------------------------------------------------------------------
# 3a) Insert a SKU for every product
products = sa.table("products", sa.column("id", postgresql.UUID(as_uuid=True)))
stock_keeping_units = sa.table(
"stock_keeping_units",
sa.column("id", postgresql.UUID(as_uuid=True)),
sa.column("product_id", postgresql.UUID(as_uuid=True)),
sa.column("is_not_available", sa.Boolean()),
sa.column("sort_order", sa.Integer()),
)
op.execute(
stock_keeping_units.insert().from_select(
["id", "product_id", "is_not_available", "sort_order"],
sa.select(
sa.func.gen_random_uuid(),
products.c.id,
sa.false(),
sa.literal(0),
),
)
)
# 3b) Migrate product_versions -> sku_versions, mapping by product_id to the single SKU
# NOTE: this assumes product_versions still has the legacy SKU-ish columns.
pv = sa.table(
"product_versions",
sa.column("id", postgresql.UUID(as_uuid=True)),
sa.column("product_id", postgresql.UUID(as_uuid=True)),
sa.column("units", sa.Unicode()),
sa.column("price", sa.Numeric(15, 2)),
sa.column("quantity", sa.Numeric(15, 2)),
sa.column("has_happy_hour", sa.Boolean()),
sa.column("is_not_available", sa.Boolean()),
sa.column("sort_order", sa.Integer()),
sa.column("menu_category_id", postgresql.UUID(as_uuid=True)),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
# Insert one sku_versions row per product_versions row
sku_versions = sa.table(
"sku_versions",
sa.column("id", postgresql.UUID(as_uuid=True)),
sa.column("sku_id", postgresql.UUID(as_uuid=True)),
sa.column("units", sa.Unicode()),
sa.column("fraction", sa.Numeric(15, 5)),
sa.column("product_yield", sa.Numeric(15, 5)),
sa.column("cost_price", sa.Numeric(15, 2)),
sa.column("sale_price", sa.Numeric(15, 2)),
sa.column("has_happy_hour", sa.Boolean()),
sa.column("sort_order", sa.Integer()),
sa.column("menu_category_id", postgresql.UUID(as_uuid=True)),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
# Join: product_versions.product_id -> stock_keeping_units.product_id (single SKU per product)
# Mapping rules (based on old vs new models you showed):
# - units -> units
# - fraction -> 1.0 (since old PV had quantity/units rather than fraction)
# - product_yield -> quantity (old PV.quantity)
# - cost_price -> 0.0 (not present in old PV)
# - sale_price -> price (old PV.price)
# - has_happy_hour -> has_happy_hour
# - sort_order -> sort_order
# - menu_category_id -> menu_category_id
# - valid_from/till -> valid_from/till
#
# Also propagate is_not_available to the SKU row:
op.execute(
sa.text(
"""
UPDATE stock_keeping_units sku
SET is_not_available = pv.is_not_available,
sort_order = pv.sort_order
FROM product_versions pv
WHERE pv.product_id = sku.product_id
AND pv.valid_till IS NULL
"""
)
)
op.execute(
sku_versions.insert().from_select(
[
"id",
"sku_id",
"units",
"fraction",
"product_yield",
"cost_price",
"sale_price",
"has_happy_hour",
"menu_category_id",
"valid_from",
"valid_till",
],
sa.select(
sa.func.gen_random_uuid(),
stock_keeping_units.c.id,
sa.func.coalesce(sa.func.nullif(sa.func.btrim(pv.c.units), ""), sa.literal("por")),
sa.cast(1.0, sa.Numeric(15, 5)),
sa.cast(1.0, sa.Numeric(15, 5)),
sa.cast(0.0, sa.Numeric(15, 2)),
sa.cast(pv.c.price, sa.Numeric(15, 2)),
pv.c.has_happy_hour,
pv.c.menu_category_id,
pv.c.valid_from,
pv.c.valid_till,
).select_from(pv.join(stock_keeping_units, stock_keeping_units.c.product_id == pv.c.product_id)),
)
)
# ---------------------------------------------------------------------
# 4) Update foreign keys from product_versions to sku_version
# ---------------------------------------------------------------------
op.drop_constraint(op.f("fk_inventories_product_id_products"), "inventories", type_="foreignkey")
op.alter_column("inventories", "product_id", new_column_name="sku_id")
op.execute(
sa.text(
"""
UPDATE inventories i
SET sku_id = sku.id
FROM stock_keeping_units sku
WHERE i.sku_id = sku.product_id
"""
)
)
op.create_foreign_key(
op.f("fk_inventories_sku_id_stock_keeping_units"), "inventories", "stock_keeping_units", ["sku_id"], ["id"]
)
# ---------------------------------------------------------------------
# 5) Drop old columns from product_versions
# ---------------------------------------------------------------------
op.alter_column("product_versions", "units", new_column_name="fraction_units")
op.drop_constraint(op.f("fk_products_menu_category_id_menu_categories"), "product_versions", type_="foreignkey")
op.create_check_constraint(
"name_not_blank",
"product_versions",
"length(trim(name)) >= 1",
)
for col in ["price", "quantity", "has_happy_hour", "is_not_available", "menu_category_id", "sort_order"]:
op.drop_column("product_versions", col)
def downgrade():
# You can implement if you need it; left explicit to avoid accidental data loss assumptions.
raise NotImplementedError("Downgrade not implemented")
@@ -0,0 +1,271 @@
"""devices1
Revision ID: 34fe3d061c5f
Revises: 8c06ac60d125
Create Date: 2020-10-27 19:38:48.445908
"""
from datetime import date, timedelta
import sqlalchemy as sa
# revision identifiers, used by Alembic.
from sqlalchemy import column, func, select, table, text
from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects.postgresql import insert as pg_insert
from alembic import op
revision = "34fe3d061c5f"
down_revision = "8c06ac60d125"
branch_labels = None
depends_on = None
def update_sale_categories():
product = table(
"products",
column("product_id", postgresql.UUID(as_uuid=True)),
column("sale_category_id", postgresql.UUID(as_uuid=True)),
column("sale_category_name", sa.Unicode(length=255)),
)
sale_category = table(
"sale_categories", column("id", postgresql.UUID(as_uuid=True)), column("name", sa.Unicode(length=255))
)
op.execute(
product.update(
values={
"sale_category_id": select([sale_category.c.id]).where(
product.c.sale_category_name == sale_category.c.name
)
}
)
)
with op.batch_alter_table("products") as batch_op:
batch_op.alter_column("sale_category_id", nullable=False)
batch_op.drop_column("sale_category_name")
def update_section_printers():
section_printer = table(
"section_printers",
column("section_id", postgresql.UUID(as_uuid=True)),
column("section_name", sa.Unicode(length=255)),
column("printer_id", postgresql.UUID(as_uuid=True)),
column("printer_name", sa.Unicode(length=255)),
)
section = table("sections", column("id", postgresql.UUID(as_uuid=True)), column("name", sa.Unicode(length=255)))
printer = table(
"printers",
column("id", postgresql.UUID(as_uuid=True)),
column("name", sa.Unicode(length=255)),
column("cut_code", sa.Unicode(length=255)),
)
op.execute(
section_printer.update(
values={"section_id": select([section.c.id]).where(section_printer.c.section_name == section.c.name)}
)
)
op.execute(
section_printer.update(
values={"printer_id": select([printer.c.id]).where(section_printer.c.printer_name == printer.c.name)}
)
)
op.execute(printer.update(values={"cut_code": "\x1d\x56\x41\x03"}))
with op.batch_alter_table("section_printers") as batch_op:
batch_op.alter_column("section_id", nullable=False)
batch_op.drop_column("section_name")
batch_op.alter_column("printer_id", nullable=False)
batch_op.drop_column("printer_name")
def add_all_roles_to_owner():
r = table(
"roles",
column("id", postgresql.UUID(as_uuid=True)),
column("name", sa.Unicode(length=255)),
)
p = table(
"permissions",
column("id", postgresql.UUID(as_uuid=True)),
column("name", sa.Unicode(length=255)),
)
rp = table(
"role_permissions",
column("id", postgresql.UUID(as_uuid=True)),
column("permission_id", postgresql.UUID(as_uuid=True)),
column("role_id", postgresql.UUID(as_uuid=True)),
)
op.execute(
pg_insert(rp)
.from_select(
[rp.c.role_id, rp.c.permission_id],
select([select([func.distinct(r.c.id)]).where(r.c.name.ilike("Owner")).as_scalar(), p.c.id]),
)
.on_conflict_do_nothing(),
)
def rename_permissions():
p = table(
"permissions",
column("id", postgresql.UUID(as_uuid=True)),
column("name", sa.Unicode(length=255)),
)
rp = table(
"role_permissions",
column("id", postgresql.UUID(as_uuid=True)),
column("permission_id", postgresql.UUID(as_uuid=True)),
column("role_id", postgresql.UUID(as_uuid=True)),
)
op.execute(p.update().where(p.c.name == "Accounts Audit").values(name="Audit"))
op.execute(p.update().where(p.c.name == "Beer Consumption").values(name="Beer Sale Report"))
op.execute(p.update().where(p.c.name == "Bill Details").values(name="Bill Settlement Report"))
op.execute(p.update().where(p.c.name == "Cashier Checkout").values(name="Cashier Report"))
op.execute(p.update().where(p.c.name == "Sales Detail").values(name="Product Sale Report"))
op.execute(p.update().where(p.c.name == "Sales Analysis").values(name="Sale Report"))
op.execute(p.update().where(p.c.name == "Tax Analysis").values(name="Tax Report"))
op.execute(p.update().where(p.c.name == "Machines").values(name="Devices"))
op.execute(p.update().where(p.c.name == "Tables").values(name="Sections"))
op.execute(
rp.delete().where(
rp.c.permission_id == select([p.c.id]).where(p.c.name.ilike("Void or Reprinted Bill Report")).as_scalar()
)
)
op.execute(rp.delete().where(rp.c.permission_id == select([p.c.id]).where(p.c.name.ilike("Roles")).as_scalar()))
op.execute(
rp.delete().where(rp.c.permission_id == select([p.c.id]).where(p.c.name.ilike("NC Product")).as_scalar())
)
op.execute(
rp.delete().where(rp.c.permission_id == select([p.c.id]).where(p.c.name.ilike("Change Rate")).as_scalar())
)
op.execute(p.delete().where(p.c.name == "Void or Reprinted Bill Report"))
op.execute(p.delete().where(p.c.name == "Roles"))
op.execute(p.delete().where(p.c.name == "NC Product"))
op.execute(p.delete().where(p.c.name == "Change Rate"))
def update_settlements():
s = table(
"settlements",
column("voucher_id", postgresql.UUID(as_uuid=True)),
column("settled", sa.Integer()),
)
v = table(
"vouchers",
sa.Column("id", postgresql.UUID(as_uuid=True)),
sa.Column("voucher_type", sa.Integer()),
)
op.execute(
s.update()
.values(settled=9)
.where(s.c.voucher_id == v.c.id)
.where(v.c.voucher_type == 5)
.where(s.c.settled == 1)
)
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
update_sale_categories()
update_section_printers()
# Migration to temporal products
op.drop_constraint("fk_inventories_product_id_products", "inventories", type_="foreignkey")
op.drop_constraint(
"fk_modifier_categories_products_product_id_products", "modifier_categories_products", type_="foreignkey"
)
op.drop_constraint("uq_products_name", "products", type_="unique")
op.drop_constraint("pk_products", "products", type_="primary")
op.rename_table("products", "product_versions")
with op.batch_alter_table("product_versions") as batch_op:
(batch_op.add_column(sa.Column("product_id", postgresql.UUID(as_uuid=True), nullable=True)),)
batch_op.add_column(sa.Column("valid_from", sa.Date(), nullable=True))
batch_op.add_column(sa.Column("valid_till", sa.Date(), nullable=True))
batch_op.create_primary_key("pk_product_versions", ["id"])
p = table(
"product_versions",
column("is_active", sa.Boolean()),
column("valid_till", sa.Date()),
)
op.execute(
p.update().values(valid_till=date.today() - timedelta(days=1)).where(p.c.is_active == False) # noqa E712
)
op.drop_column("product_versions", "is_active")
prod_v = table(
"product_versions",
column("id", postgresql.UUID(as_uuid=True)),
column("product_id", postgresql.UUID(as_uuid=True)),
)
op.execute(prod_v.update(values={"product_id": prod_v.c.id}))
op.create_table(
"products",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_products")),
)
op.alter_column("product_versions", "product_id", nullable=False)
prod = table(
"product_versions",
column("id", postgresql.UUID(as_uuid=True)),
column("name", sa.Unicode(length=255)),
column("units", sa.Unicode(length=255)),
column("valid_from", sa.Date()),
column("valid_till", sa.Date()),
)
pv = table(
"products",
column("id", postgresql.UUID(as_uuid=True)),
)
op.execute(pv.insert().from_select([pv.c.id], select([prod.c.id])))
op.create_foreign_key(
op.f("fk_product_versions_product_id_products"), "product_versions", "products", ["product_id"], ["id"]
)
op.create_foreign_key(op.f("fk_inventories_product_id_products"), "inventories", "products", ["product_id"], ["id"])
op.create_foreign_key(
op.f("fk_modifier_categories_products_product_id_products"),
"modifier_categories_products",
"products",
["product_id"],
["id"],
)
op.alter_column("modifier_categories_products", "modifier_categories_id", new_column_name="modifier_category_id")
op.drop_constraint("uq_modifier_categories_products_product_id", "modifier_categories_products", type_="unique")
op.create_unique_constraint(
op.f("uq_modifier_categories_products_product_id"),
"modifier_categories_products",
["product_id", "modifier_category_id"],
)
op.create_exclude_constraint(
"uq_product_versions_name",
"product_versions",
(prod.c.name, "="),
(prod.c.units, "="),
(func.daterange(prod.c.valid_from, prod.c.valid_till, text("'[]'")), "&&"),
)
rename_permissions()
add_all_roles_to_owner()
update_settlements()
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("products", sa.Column("sale_category_name", sa.Unicode(length=255), nullable=False))
op.drop_constraint("fk_products_sale_category_id_sale_categories", "products", type_="foreignkey")
op.drop_column("products", "sale_category_id")
# ### end Alembic commands ###
@@ -0,0 +1,32 @@
"""user
Revision ID: 3609c44430c8
Revises: 81d94c5223a7
Create Date: 2021-09-28 09:18:10.666701
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "3609c44430c8"
down_revision = "81d94c5223a7"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint("uq_users_name", "users", type_="unique")
op.create_index("uq_users_name", "users", [sa.text("lower(name)")], unique=True)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index("uq_users_name", table_name="users")
op.create_unique_constraint("uq_users_name", "users", ["name"])
# ### end Alembic commands ###
@@ -0,0 +1,147 @@
"""bundles
Revision ID: 367ecf7b898f
Revises: 1e0daf6bc1ae
Create Date: 2026-01-28 16:11:21.874409
"""
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from alembic import op
# revision identifiers, used by Alembic.
revision = "367ecf7b898f"
down_revision = "1e0daf6bc1ae"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"bundle_items",
sa.Column("id", sa.Uuid(), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("bundle_id", sa.Uuid(), nullable=False),
sa.Column("item_id", sa.Uuid(), nullable=False),
sa.Column("quantity", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("sale_price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("print_in_bill", sa.Boolean(), server_default=sa.text("true"), nullable=False),
sa.Column("valid_from", sa.Date(), nullable=True),
sa.Column("valid_till", sa.Date(), nullable=True),
postgresql.ExcludeConstraint(
(sa.column("bundle_id"), "="),
(sa.column("item_id"), "="),
(sa.text("daterange(valid_from, valid_till, '[]')"), "&&"),
using="gist",
name=op.f("uq_bundle_items_bundle_id_item_id"),
),
sa.ForeignKeyConstraint(
["bundle_id"], ["stock_keeping_units.id"], name=op.f("fk_bundle_items_bundle_id_stock_keeping_units")
),
sa.ForeignKeyConstraint(
["item_id"], ["stock_keeping_units.id"], name=op.f("fk_bundle_items_item_id_stock_keeping_units")
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_bundle_items")),
)
inventory_type = sa.Enum("regular", "bundle", "bundle_item", name="inventorytype")
inventory_type.create(op.get_bind())
op.add_column("inventories", sa.Column("type", inventory_type, server_default=sa.text("'regular'"), nullable=False))
op.add_column("inventories", sa.Column("parent_id", sa.Uuid(), nullable=True))
op.drop_constraint(op.f("uq_inventories_kot_id"), "inventories", type_="unique")
op.create_unique_constraint(
op.f("uq_inventories_kot_id"),
"inventories",
["kot_id", "sku_id", "is_happy_hour", "type", "parent_id"],
postgresql_nulls_not_distinct=True,
)
op.create_foreign_key(
op.f("fk_inventories_parent_id_inventories"), "inventories", "inventories", ["parent_id"], ["id"]
)
op.add_column(
"stock_keeping_units", sa.Column("is_bundle", sa.Boolean(), server_default=sa.text("false"), nullable=False)
)
mc = sa.table(
"menu_categories",
sa.column("id", sa.Uuid()),
sa.column("name", sa.Unicode(length=255)),
sa.column("is_active", sa.Boolean()),
sa.column("is_fixture", sa.Boolean()),
sa.column("sort_order", sa.Integer()),
)
op.execute(
mc.insert().values(
id="6752ed9d-6f1a-4941-940a-17759dcc6720",
name="Bundles",
is_active=True,
is_fixture=True,
sort_order=0,
)
)
customers = sa.table(
"customers",
sa.column("id", sa.Uuid),
sa.column("name", sa.Text),
sa.column("phone", sa.Text),
)
guest_book = sa.table(
"guest_book",
sa.column("customer_id", sa.Uuid),
)
voucher = sa.table(
"vouchers",
sa.column("customer_id", sa.Uuid),
)
bad_customer_ids = sa.select(customers.c.id).where(
sa.func.length(sa.func.trim(customers.c.phone)) == 0,
)
op.execute(guest_book.delete().where(guest_book.c.customer_id.in_(bad_customer_ids)))
op.execute(voucher.update().where(voucher.c.customer_id.in_(bad_customer_ids)).values(customer_id=None))
op.execute(customers.delete().where(customers.c.id.in_(bad_customer_ids)))
op.execute(
customers.update()
.where(
sa.func.length(sa.func.trim(customers.c.name)) == 0,
)
.values(name=customers.c.phone)
)
with op.batch_alter_table("customers") as batch_op:
batch_op.create_check_constraint("name_not_blank", sa.text("length(trim(both from name)) >= 1"))
batch_op.create_check_constraint("phone_not_blank", sa.text("length(trim(both from phone)) >= 1"))
def downgrade():
op.drop_table("bundle_items")
op.drop_column("stock_keeping_units", "is_bundle")
op.drop_constraint(op.f("fk_inventories_parent_id_inventories"), "inventories", type_="foreignkey")
op.drop_constraint(op.f("uq_inventories_kot_id"), "inventories", type_="unique")
op.create_unique_constraint(
op.f("uq_inventories_kot_id"),
"inventories",
["kot_id", "sku_id", "is_happy_hour", "price"],
postgresql_nulls_not_distinct=False,
)
op.drop_column("inventories", "parent_id")
op.drop_column("inventories", "type")
mc = sa.table(
"menu_categories",
sa.column("id", sa.Uuid()),
)
op.execute(mc.delete().where(mc.c.id == "6752ed9d-6f1a-4941-940a-17759dcc6720"))
with op.batch_alter_table("customers") as batch_op:
batch_op.drop_constraint("name_not_blank", type_="check")
batch_op.drop_constraint("phone_not_blank", type_="check")
@@ -0,0 +1,149 @@
"""Change Settle Options
Revision ID: 48bc1c7c07ce
Revises: 34fe3d061c5f
Create Date: 2020-12-10 08:29:39.175097
"""
import sqlalchemy as sa
# revision identifiers, used by Alembic.
from sqlalchemy import column, table
from sqlalchemy.sql import expression
from alembic import op
revision = "48bc1c7c07ce"
down_revision = "34fe3d061c5f"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
reporting_level = sa.Enum("Skip", "Aggregate", "Detailed", name="reporting_level")
reporting_level.create(op.get_bind())
voucher_type = sa.Enum("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", name="voucher_type")
voucher_type.create(op.get_bind())
op.add_column(
"settle_options", sa.Column("has_reason", sa.Boolean(), nullable=False, server_default=expression.false())
)
op.add_column(
"settle_options", sa.Column("is_fixture", sa.Boolean(), nullable=False, server_default=expression.false())
)
op.add_column(
"settle_options",
sa.Column(
"reporting_level",
reporting_level,
nullable=False,
server_default="Skip",
),
)
op.add_column(
"settle_options",
sa.Column(
"voucher_type",
voucher_type,
nullable=False,
server_default="KOT",
),
)
so = table(
"settle_options",
column("id", sa.Integer()),
column("is_fixture", sa.Boolean()),
column("has_reason", sa.Boolean()),
column("reporting_level", reporting_level),
column("voucher_type", voucher_type),
)
v = table(
"vouchers",
column("voucher_type_old", sa.Integer()),
column("voucher_type", voucher_type),
)
op.execute(
so.update()
.where(so.c.id == 1) # name = "Unsettled"
.values(reporting_level="Detailed", voucher_type="KOT", is_fixture=True)
)
op.execute(
so.update()
.where(so.c.id == 2) # name = "Cash"
.values(reporting_level="Aggregate", voucher_type="REGULAR_BILL", is_fixture=True)
)
op.execute(
so.update()
.where(so.c.id == 3) # name = "Credit Card"
.values(reporting_level="Detailed", voucher_type="REGULAR_BILL", is_fixture=False)
)
op.execute(
so.update()
.where(so.c.id == 4) # name = "No Charge"
.values(reporting_level="Detailed", voucher_type="NO_CHARGE", has_reason=True, is_fixture=True)
)
op.execute(
so.update()
.where(so.c.id == 5) # name = "Bill To Company"
.values(reporting_level="Detailed", voucher_type="REGULAR_BILL", is_fixture=False)
)
op.execute(
so.update()
.where(so.c.id == 6) # name = "Tip"
.values(reporting_level="Detailed", voucher_type="REGULAR_BILL", is_fixture=True)
)
op.execute(
so.update()
.where(so.c.id == 7) # name = "Round Off"
.values(reporting_level="Skip", voucher_type="KOT", is_fixture=True)
)
op.execute(
so.update()
.where(so.c.id == 8) # name = "Amount"
.values(reporting_level="Skip", voucher_type="KOT", is_fixture=True)
)
op.execute(
so.update()
.where(so.c.id == 9) # name = "Void"
.values(reporting_level="Detailed", voucher_type="VOID", has_reason=True, is_fixture=True)
)
op.execute(
so.update()
.where(so.c.id == 10) # name = "Staff"
.values(reporting_level="Detailed", voucher_type="STAFF", has_reason=True, is_fixture=True)
)
op.drop_column("settle_options", "is_print")
op.drop_column("settle_options", "show_in_choices")
op.drop_column("settle_options", "display_group")
op.alter_column("vouchers", "voucher_type", new_column_name="voucher_type_old")
op.add_column(
"vouchers",
sa.Column(
"voucher_type",
voucher_type,
nullable=False,
server_default="KOT",
),
)
op.execute(v.update().where(v.c.voucher_type_old == 0).values(voucher_type="KOT"))
op.execute(v.update().where(v.c.voucher_type_old == 1).values(voucher_type="REGULAR_BILL"))
op.execute(v.update().where(v.c.voucher_type_old == 2).values(voucher_type="NO_CHARGE"))
op.execute(v.update().where(v.c.voucher_type_old == 4).values(voucher_type="STAFF"))
op.execute(v.update().where(v.c.voucher_type_old == 5).values(voucher_type="VOID"))
op.drop_column("vouchers", "voucher_type_old")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
@@ -0,0 +1,202 @@
"""tax regimes
Revision ID: 5967e6603c3e
Revises: f135019a4ebf
Create Date: 2023-02-22 03:55:58.764092
"""
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects.postgresql import ENUM
from alembic import op
# revision identifiers, used by Alembic.
revision = "5967e6603c3e"
down_revision = "f135019a4ebf"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
create_regime_table()
create_bills_table()
update_taxes()
op.drop_index("ix_vouchers_date", table_name="vouchers")
op.drop_constraint("uq_vouchers_bill_id", "vouchers", type_="unique")
op.drop_column("vouchers", "bill_id")
p = sa.table(
"permissions",
sa.column("id", postgresql.UUID(as_uuid=True)),
sa.column("name", sa.Unicode(length=255)),
)
op.execute(p.insert().values(id="28e2194d-b830-4282-b8c5-3b8522b48894", name="Regimes"))
# ### end Alembic commands ###
def create_regime_table():
op.create_table(
"regimes",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("header", sa.Unicode(length=255), nullable=False),
sa.Column("prefix", sa.Unicode(length=255), nullable=False),
sa.Column("is_fixture", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_regimes")),
sa.UniqueConstraint("name", name=op.f("uq_regimes_name")),
sa.UniqueConstraint("prefix", name=op.f("uq_regimes_prefix")),
)
re = sa.table(
"regimes",
sa.column("id", sa.Integer()),
sa.column("name", sa.Unicode(length=255)),
sa.column("header", sa.Unicode(length=255)),
sa.column("prefix", sa.Unicode(length=255)),
sa.column("is_fixture", sa.Boolean()),
)
op.execute(
re.insert().values(
id=0,
name="KOT",
header="",
prefix="K",
is_fixture=True,
)
)
op.execute(
re.insert().values(
id=1,
name="Combined",
header="",
prefix="INV",
is_fixture=True,
)
)
op.execute(
re.insert().values(
id=2,
name="No Charge",
header="NO CHARGE - THIS IS NOT A BILL - DON'T PAY",
prefix="NC",
is_fixture=True,
)
)
op.execute(
re.insert().values(
id=4,
name="Staff",
header=" STAFF CONSUMPTION -- THIS IS NOT A BILL ",
prefix="ST",
is_fixture=True,
)
)
op.execute(
re.insert().values(
id=8,
name="Void",
header="",
prefix="V",
is_fixture=True,
)
)
op.execute(
re.insert().values(
id=16,
name="Goods and Service Tax",
header=" GSTIN: XXXXXXXXXXXXXXX \n Retail Invoice ",
prefix="GST",
is_fixture=False,
)
)
op.execute(
re.insert().values(
id=32,
name="Value Added Tax",
header=" TIN: XXXXXXXXXXX \n Retail Invoice ",
prefix="VAT",
is_fixture=False,
)
)
def create_bills_table():
voucher_type = ENUM("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", name="voucher_type", create_type=False)
op.create_table(
"bills",
sa.Column("id", sa.UUID(), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("regime_id", sa.Integer(), nullable=False),
sa.Column("voucher_id", sa.UUID(), nullable=False),
sa.Column("is_valid", sa.Boolean(), nullable=False),
sa.Column("bill_number", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(["regime_id"], ["regimes.id"], name=op.f("fk_bills_regime_id_regimes")),
sa.ForeignKeyConstraint(["voucher_id"], ["vouchers.id"], name=op.f("fk_bills_voucher_id_vouchers")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_bills")),
sa.UniqueConstraint("regime_id", "bill_number", name=op.f("uq_bills_regime_id")),
sa.UniqueConstraint("voucher_id", "regime_id", name=op.f("uq_bills_voucher_id")),
)
b = sa.table(
"bills",
sa.column("regime_id", sa.Integer()),
sa.column("voucher_id", sa.UUID()),
sa.column("is_valid", sa.Boolean()),
sa.column("bill_number", sa.Integer()),
)
v = sa.table(
"vouchers",
sa.column("id", sa.UUID()),
sa.column("voucher_type", voucher_type),
sa.column("bill_id", sa.Integer()),
sa.column("kot_id", sa.Integer()),
)
op.execute(
b.insert().from_select(
[b.c.regime_id, b.c.voucher_id, b.c.is_valid, b.c.bill_number],
sa.select(
sa.case(
(v.c.voucher_type == "KOT", sa.text("0")),
(v.c.voucher_type == "REGULAR_BILL", sa.text("1")),
(v.c.voucher_type == "NO_CHARGE", sa.text("2")),
(v.c.voucher_type == "STAFF", sa.text("4")),
(v.c.voucher_type == "VOID", sa.text("8")),
),
v.c.id,
sa.text("True"),
sa.case((v.c.bill_id == None, v.c.kot_id), else_=v.c.bill_id), # noqa: E711
).where(
v.c.bill_id != None # noqa: E711
),
)
)
def update_taxes():
op.add_column("taxes", sa.Column("regime_id", sa.Integer(), nullable=True))
ta = sa.table(
"taxes",
sa.column("regime_id", sa.Integer()),
)
op.execute(ta.update().values(regime_id=1))
op.alter_column("taxes", "regime_id", existing_type=sa.Integer(), nullable=False)
op.create_foreign_key(op.f("fk_taxes_regime_id_regimes"), "taxes", "regimes", ["regime_id"], ["id"])
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("vouchers", sa.Column("bill_id", sa.INTEGER(), autoincrement=False, nullable=True))
op.add_column("vouchers", sa.Column("date", postgresql.TIMESTAMP(), autoincrement=False, nullable=False))
op.drop_index(op.f("ix_vouchers_creation_date"), table_name="vouchers")
op.create_unique_constraint("uq_vouchers_bill_id", "vouchers", ["bill_id", "voucher_type"])
op.create_index("ix_vouchers_date", "vouchers", ["date"], unique=False)
op.drop_constraint(op.f("fk_taxes_regime_id_regimes"), "taxes", type_="foreignkey")
op.drop_column("taxes", "regime_id")
op.drop_index(op.f("ix_bills_date"), table_name="bills")
op.drop_table("bills")
op.drop_table("regimes")
# ### end Alembic commands ###
@@ -0,0 +1,40 @@
"""role includes
Revision ID: 5cb65066be86
Revises: 367ecf7b898f
Create Date: 2026-02-11 08:21:01.679893
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "5cb65066be86"
down_revision = "367ecf7b898f"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"role_includes",
sa.Column("id", sa.Uuid(), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("role_id", sa.Uuid(), nullable=False),
sa.Column("included_role_id", sa.Uuid(), nullable=False),
sa.CheckConstraint("role_id <> included_role_id", name=op.f("ck_role_includes_no_self_include")),
sa.ForeignKeyConstraint(
["included_role_id"], ["roles.id"], name=op.f("fk_role_includes_included_role_id_roles")
),
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], name=op.f("fk_role_includes_role_id_roles")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_role_includes")),
sa.UniqueConstraint("role_id", "included_role_id", name=op.f("uq_role_includes_role_id")),
sa.Index(op.f("ix_role_includes_role_id"), "role_id"),
sa.Index(op.f("ix_role_includes_included_role_id"), "included_role_id"),
)
def downgrade():
op.drop_table("role_includes")
@@ -0,0 +1,182 @@
"""booking
Revision ID: 7e9944b430d6
Revises: 1d95eb10ea20
Create Date: 2023-08-19 14:25:40.206187
"""
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from alembic import op
# revision identifiers, used by Alembic.
revision = "7e9944b430d6"
down_revision = "1d95eb10ea20"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("customers", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column(
"customers", "phone", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column(
"customers", "address", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=True
)
op.alter_column("devices", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column(
"food_tables", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column(
"menu_categories", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column(
"modifier_categories", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column("modifiers", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
overviewstatus = sa.Enum("running", "printed", name="overviewstatus")
overviewstatus.create(op.get_bind())
op.execute("ALTER TABLE overview ALTER COLUMN status TYPE overviewstatus USING status::text::overviewstatus;")
op.alter_column(
"overview",
"status",
existing_type=sa.VARCHAR(length=255),
type_=overviewstatus,
existing_nullable=False,
)
op.alter_column(
"permissions", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column("printers", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column(
"printers", "address", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column(
"printers", "cut_code", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column(
"product_versions", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column(
"product_versions", "units", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column("regimes", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column("regimes", "header", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column("regimes", "prefix", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column("roles", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column(
"sale_categories", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column("sections", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column("settings", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column("settings", "data", existing_type=postgresql.JSON(astext_type=sa.Text()), nullable=False)
op.alter_column(
"settle_options", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False
)
op.alter_column("taxes", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column("users", "name", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
op.alter_column("users", "password", existing_type=sa.VARCHAR(length=60), type_=sa.Text(), existing_nullable=False)
op.alter_column(
"vouchers", "narration", existing_type=sa.VARCHAR(length=1000), type_=sa.Text(), existing_nullable=True
)
op.alter_column("vouchers", "reason", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=True)
guestbooktype = sa.Enum("walk_in", "booking", "arrived", name="guestbooktype")
guestbooktype.create(op.get_bind())
gbt = sa.table(
"guest_book",
sa.column("last_edit_date", sa.DateTime()),
sa.column("arrival_date", sa.DateTime()),
sa.column("type", guestbooktype),
)
with op.batch_alter_table("guest_book") as batch_op:
batch_op.add_column(sa.Column("booking_date", sa.DateTime(), nullable=True))
batch_op.alter_column("creation_date", new_column_name="last_edit_date")
batch_op.add_column(sa.Column("arrival_date", sa.DateTime(), nullable=True))
batch_op.add_column(sa.Column("notes", sa.Text(), nullable=True))
batch_op.add_column(sa.Column("type", guestbooktype, nullable=True))
op.execute(gbt.update().values(arrival_date=sa.text("last_edit_date")))
op.execute(gbt.update().values(type="walk_in"))
with op.batch_alter_table("guest_book") as batch_op:
batch_op.alter_column("type", existing_type=guestbooktype, nullable=False)
batch_op.create_check_constraint("nulls", sa.text("num_nonnulls(booking_date, arrival_date) > 0"))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column("vouchers", "reason", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=True)
op.alter_column(
"vouchers", "narration", existing_type=sa.Text(), type_=sa.VARCHAR(length=1000), existing_nullable=True
)
op.alter_column("users", "password", existing_type=sa.Text(), type_=sa.VARCHAR(length=60), existing_nullable=False)
op.alter_column("users", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column("taxes", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column(
"settle_options", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column("settings", "data", existing_type=postgresql.JSON(astext_type=sa.Text()), nullable=True)
op.alter_column("settings", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column("sections", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column(
"sale_categories", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column("roles", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column("regimes", "prefix", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column("regimes", "header", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column("regimes", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column(
"product_versions", "units", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column(
"product_versions", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column(
"printers", "cut_code", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column(
"printers", "address", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column("printers", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column(
"permissions", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column(
"overview",
"status",
existing_type=sa.Enum("running", "printed", name="overviewstatus"),
type_=sa.VARCHAR(length=255),
existing_nullable=False,
)
op.alter_column("modifiers", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column(
"modifier_categories", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column(
"menu_categories", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.add_column("guest_book", sa.Column("creation_date", postgresql.TIMESTAMP(), autoincrement=False, nullable=False))
op.drop_column("guest_book", "type_")
op.drop_column("guest_book", "notes")
op.drop_column("guest_book", "last_edit_date")
op.drop_column("guest_book", "date")
op.alter_column(
"food_tables", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column("devices", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
op.alter_column(
"customers", "address", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=True
)
op.alter_column(
"customers", "phone", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False
)
op.alter_column("customers", "name", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
# ### end Alembic commands ###
@@ -0,0 +1,75 @@
"""section printer
Revision ID: 81d94c5223a7
Revises: c123dbf9c659
Create Date: 2021-08-17 12:10:01.082632
"""
import sqlalchemy as sa
from sqlalchemy import column, table
from sqlalchemy.dialects import postgresql
from alembic import op
# revision identifiers, used by Alembic.
revision = "81d94c5223a7"
down_revision = "c123dbf9c659"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
sp = table(
"section_printers",
column("id", postgresql.UUID(as_uuid=True)),
)
op.execute(sp.delete())
op.add_column("section_printers", sa.Column("sale_category_id", postgresql.UUID(as_uuid=True), nullable=True))
op.drop_constraint("uq_section_printers_menu_category_id", "section_printers", type_="unique")
op.create_unique_constraint(
op.f("uq_section_printers_sale_category_id"), "section_printers", ["sale_category_id", "section_id"]
)
op.drop_constraint("fk_section_printers_menu_category_id_menu_categories", "section_printers", type_="foreignkey")
op.create_foreign_key(
op.f("fk_section_printers_sale_category_id_sale_categories"),
"section_printers",
"sale_categories",
["sale_category_id"],
["id"],
)
op.drop_column("section_printers", "menu_category_id")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
sp = table(
"section_printers",
column("id", postgresql.UUID(as_uuid=True)),
)
op.execute(sp.delete())
op.add_column(
"section_printers", sa.Column("menu_category_id", postgresql.UUID(), autoincrement=False, nullable=True)
)
op.drop_constraint(
op.f("fk_section_printers_sale_category_id_sale_categories"), "section_printers", type_="foreignkey"
)
op.create_foreign_key(
"fk_section_printers_menu_category_id_menu_categories",
"section_printers",
"menu_categories",
["menu_category_id"],
["id"],
)
op.drop_constraint(op.f("uq_section_printers_sale_category_id"), "section_printers", type_="unique")
op.create_unique_constraint(
"uq_section_printers_menu_category_id", "section_printers", ["menu_category_id", "section_id"]
)
op.drop_column("section_printers", "sale_category_id")
# ### end Alembic commands ###
@@ -0,0 +1,156 @@
"""index
Revision ID: 8260414066d6
Revises: 5cb65066be86
Create Date: 2026-02-13 06:22:39.926120
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "8260414066d6"
down_revision = "5cb65066be86"
branch_labels = None
depends_on = None
def upgrade():
# 1) Add the column (nullable for backfill)
op.add_column("sku_versions", sa.Column("product_id", sa.UUID(), nullable=True))
# 2) Define lightweight table objects (no autoload; explicit columns only)
sku_versions = sa.Table(
"sku_versions",
sa.MetaData(),
sa.Column("id", sa.UUID(), primary_key=True),
sa.Column("sku_id", sa.UUID(), nullable=False),
sa.Column("product_id", sa.UUID(), nullable=True),
sa.Column("valid_from", sa.Date(), nullable=True),
sa.Column("valid_till", sa.Date(), nullable=True),
sa.Column("units", sa.Unicode(), nullable=False),
)
stock_keeping_units = sa.Table(
"stock_keeping_units",
sa.MetaData(),
sa.Column("id", sa.UUID(), primary_key=True),
sa.Column("product_id", sa.UUID(), nullable=False),
)
# 3) Backfill via SQLAlchemy Core UPDATE..SET..(SELECT ...)
product_id_subq = (
sa.select(stock_keeping_units.c.product_id)
.where(stock_keeping_units.c.id == sku_versions.c.sku_id)
.scalar_subquery()
)
backfill_stmt = (
sa.update(sku_versions).values(product_id=product_id_subq).where(sku_versions.c.product_id.is_(None))
)
op.execute(backfill_stmt)
# 5) Enforce NOT NULL at schema level
op.alter_column("sku_versions", "product_id", nullable=False)
# 6) Trigger keeps product_id in sync when sku_id changes
op.execute(
sa.text(
"""
CREATE OR REPLACE FUNCTION sku_versions_set_product_id()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
SELECT s.product_id INTO NEW.product_id
FROM stock_keeping_units s
WHERE s.id = NEW.sku_id;
IF NEW.product_id IS NULL THEN
RAISE EXCEPTION 'Invalid sku_id %, no product found', NEW.sku_id;
END IF;
RETURN NEW;
END;
$$;
"""
)
)
op.execute(sa.text("DROP TRIGGER IF EXISTS trg_sku_versions_set_product_id ON sku_versions;"))
op.execute(
sa.text(
"""
CREATE TRIGGER trg_sku_versions_set_product_id
BEFORE INSERT OR UPDATE OF sku_id
ON sku_versions
FOR EACH ROW
EXECUTE FUNCTION sku_versions_set_product_id();
"""
)
)
# 7) Exclusion constraint: product_id + units must not overlap in time
# daterange(valid_from, valid_till, '[]') overlap operator &&.
sv = sa.table(
"sku_versions",
sa.column("product_id", sa.UUID()),
sa.column("units", sa.UUID()),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
op.create_exclude_constraint(
op.f("uq_sku_versions_product_id_units"),
"sku_versions",
(sv.c.product_id, "="),
(sv.c.units, "="),
(sa.func.daterange(sv.c.valid_from, sv.c.valid_till, sa.text("'[]'")), "&&"),
)
prod = sa.table(
"product_versions",
sa.column("id", sa.UUID()),
sa.column("name", sa.Unicode(length=255)),
sa.column("fraction_units", sa.Unicode(length=255)),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
# Update the exclude constraint on product_versions to only be on the name and drop fraction_units from it
op.drop_constraint("uq_product_versions_name", "product_versions", type_="unique")
op.create_exclude_constraint(
op.f("uq_product_versions_name"),
"product_versions",
(prod.c.name, "="),
(sa.func.daterange(prod.c.valid_from, prod.c.valid_till, sa.text("'[]'")), "&&"),
)
# ### end Alembic commands ###
def downgrade():
op.drop_constraint("uq_sku_versions_product_id_units", "sku_versions", type_="exclude")
# Drop trigger + function
op.execute(sa.text("DROP TRIGGER IF EXISTS trg_sku_versions_set_product_id ON sku_versions;"))
op.execute(sa.text("DROP FUNCTION IF EXISTS sku_versions_set_product_id();"))
# Drop column
op.drop_column("sku_versions", "product_id")
prod = sa.table(
"product_versions",
sa.column("id", sa.UUID()),
sa.column("name", sa.Unicode(length=255)),
sa.column("fraction_units", sa.Unicode(length=255)),
sa.column("valid_from", sa.Date()),
sa.column("valid_till", sa.Date()),
)
op.drop_constraint("uq_product_versions_name", "product_versions", type_="unique")
op.create_exclude_constraint(
"uq_product_versions_name",
"product_versions",
(prod.c.name, "="),
(prod.c.units, "="),
(sa.func.daterange(prod.c.valid_from, prod.c.valid_till, sa.text("'[]'")), "&&"),
)
@@ -0,0 +1,706 @@
"""Initial Commit
Revision ID: 8c06ac60d125
Revises:
Create Date: 2020-06-04 08:14:34.132248
"""
import sqlalchemy as sa
from sqlalchemy import column, table
from sqlalchemy.dialects import postgresql
from alembic import op
from barker.models.db_setting import DbSetting
from barker.models.modifier_category import ModifierCategory
from barker.models.permission import Permission
from barker.models.section import Section
from barker.models.user import User # noqa: F401
# revision identifiers, used by Alembic.
revision = "8c06ac60d125"
down_revision = None
branch_labels = None
depends_on = None
def settle_options():
so = table(
"settle_options",
column("id", sa.Integer()),
column("name", sa.Unicode(length=255)),
column("show_in_choices", sa.Boolean()),
column("display_group", sa.Integer()),
column("is_print", sa.Boolean()),
)
op.execute(
so.insert().values(
id=1,
name="Unsettled",
show_in_choices=False,
display_group=1,
is_print=True,
)
)
op.execute(so.insert().values(id=2, name="Cash", show_in_choices=True, display_group=2, is_print=False))
op.execute(
so.insert().values(
id=3,
name="Credit Card",
show_in_choices=True,
display_group=2,
is_print=True,
)
)
op.execute(so.insert().values(id=4, name="No Charge", show_in_choices=True, display_group=3, is_print=True))
op.execute(
so.insert().values(
id=5,
name="Bill To Company",
show_in_choices=True,
display_group=2,
is_print=True,
)
)
op.execute(so.insert().values(id=6, name="Tip", show_in_choices=True, display_group=2, is_print=True))
op.execute(
so.insert().values(
id=7,
name="Round Off",
show_in_choices=False,
display_group=1,
is_print=False,
)
)
op.execute(so.insert().values(id=8, name="Amount", show_in_choices=False, display_group=1, is_print=False))
op.execute(so.insert().values(id=9, name="Void", show_in_choices=True, display_group=1, is_print=True))
op.execute(so.insert().values(id=10, name="Staff", show_in_choices=True, display_group=4, is_print=True))
customer = table(
"customers",
column("id", postgresql.UUID(as_uuid=True)),
column("name", sa.Unicode(length=255)),
column("phone", sa.Unicode(length=255)),
column("address", sa.Unicode(length=255)),
)
op.execute(
customer.insert().values(
id="2c716f4b-0736-429a-ad51-610d7c47cb5e",
name="Cash",
phone="",
address="",
)
)
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"customers",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("phone", sa.Unicode(length=255), nullable=False),
sa.Column("address", sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint("id", name=op.f("pk_customers")),
sa.UniqueConstraint("phone", name=op.f("uq_customers_phone")),
)
op.create_table(
"menu_categories",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("is_fixture", sa.Boolean(), nullable=False),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_menu_categories")),
sa.UniqueConstraint("name", name=op.f("uq_menu_categories_name")),
)
op.create_table(
"modifier_categories",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("minimum", sa.Integer(), nullable=False),
sa.Column("maximum", sa.Integer(), nullable=True),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_modifier_categories")),
sa.UniqueConstraint("name", name=op.f("uq_modifier_categories_name")),
)
op.create_table(
"permissions",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint("id", name=op.f("pk_permissions")),
sa.UniqueConstraint("name", name=op.f("uq_permissions_name")),
)
op.create_table(
"printers",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("address", sa.Unicode(length=255), nullable=False),
sa.Column("cut_code", sa.Unicode(length=255), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_printers")),
sa.UniqueConstraint("name", name=op.f("uq_printers_name")),
)
op.create_table(
"roles",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=True),
sa.PrimaryKeyConstraint("id", name=op.f("pk_roles")),
sa.UniqueConstraint("name", name=op.f("uq_roles_name")),
)
op.create_table(
"sections",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("is_fixture", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_sections")),
sa.UniqueConstraint("name", name=op.f("uq_sections_name")),
)
op.create_table(
"settings",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("data", sa.JSON(), nullable=True),
sa.PrimaryKeyConstraint("id", name=op.f("pk_settings")),
sa.UniqueConstraint("name", name=op.f("uq_settings_name")),
)
op.create_table(
"settle_options",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("show_in_choices", sa.Boolean(), nullable=False),
sa.Column("display_group", sa.Integer(), nullable=False),
sa.Column("is_print", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_settle_options")),
sa.UniqueConstraint("name", name=op.f("uq_settle_options_name")),
)
op.create_table(
"taxes",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("rate", sa.Numeric(precision=15, scale=5), nullable=False),
sa.Column("is_fixture", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_taxes")),
sa.UniqueConstraint("name", name=op.f("uq_taxes_name")),
)
op.create_table(
"users",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("password", sa.Unicode(length=60), nullable=False),
sa.Column("locked_out", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_users")),
sa.UniqueConstraint("name", name=op.f("uq_users_name")),
)
op.create_table(
"devices",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("section_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.Column("creation_date", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["section_id"], ["sections.id"], name=op.f("fk_devices_section_id_sections")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_devices")),
sa.UniqueConstraint("name", name=op.f("uq_devices_name")),
)
op.create_table(
"login_history",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("device_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("date", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["device_id"], ["devices.id"], name=op.f("fk_login_history_device_id_devices")),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], name=op.f("fk_login_history_user_id_users")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_login_history")),
sa.UniqueConstraint("user_id", "device_id", "date", name=op.f("uq_login_history_user_id")),
)
op.create_table(
"food_tables",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("seats", sa.Integer(), nullable=False),
sa.Column("section_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["section_id"],
["sections.id"],
name=op.f("fk_food_tables_section_id_sections"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_food_tables")),
sa.UniqueConstraint("name", name=op.f("uq_food_tables_name")),
)
op.create_table(
"guest_book",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("customer_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("pax", sa.Integer, nullable=False),
sa.Column("creation_date", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(
["customer_id"],
["customers.id"],
name=op.f("fk_guest_book_customer_id_customers"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_guest_book")),
)
op.create_table(
"modifiers",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("show_in_bill", sa.Boolean(), nullable=False),
sa.Column("price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("modifier_category_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(
["modifier_category_id"],
["modifier_categories.id"],
name=op.f("fk_modifiers_modifier_category_id_modifier_categories"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_modifiers")),
sa.UniqueConstraint("name", name=op.f("uq_modifiers_name")),
)
op.create_table(
"role_permissions",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("permission_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("role_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(
["permission_id"],
["permissions.id"],
name=op.f("fk_role_permissions_permission_id_permissions"),
),
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], name=op.f("fk_role_permissions_role_id_roles")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_role_permissions")),
sa.UniqueConstraint("permission_id", "role_id", name=op.f("uq_role_permissions_permission_id")),
)
op.create_table(
"sale_categories",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("discount_limit", sa.Numeric(precision=15, scale=5), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("tax_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(["tax_id"], ["taxes.id"], name=op.f("fk_sale_categories_tax_id_taxes")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_sale_categories")),
sa.UniqueConstraint("name", name=op.f("uq_sale_categories_name")),
)
op.create_table(
"customer_discount",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("customer_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("sale_category_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("discount", sa.Numeric(precision=15, scale=5), nullable=False),
sa.ForeignKeyConstraint(
["customer_id"], ["customers.id"], name=op.f("fk_customer_discount_customer_id_customers")
),
sa.ForeignKeyConstraint(
["sale_category_id"],
["sale_categories.id"],
name=op.f("fk_customer_discount_sale_category_id_sale_categories"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_customer_discount")),
sa.UniqueConstraint("customer_id", "sale_category_id", name=op.f("uq_customer_discount_customer_id")),
)
op.create_table(
"section_printers",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("menu_category_id", postgresql.UUID(as_uuid=True), nullable=True),
sa.Column("section_id", postgresql.UUID(as_uuid=True), nullable=True),
sa.Column("section_name", sa.Unicode(length=255), nullable=False),
sa.Column("printer_id", postgresql.UUID(as_uuid=True), nullable=True),
sa.Column("printer_name", sa.Unicode(length=255), nullable=False),
sa.Column("copies", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["menu_category_id"],
["menu_categories.id"],
name=op.f("fk_section_printers_menu_category_id_menu_categories"),
),
sa.ForeignKeyConstraint(
["printer_id"],
["printers.id"],
name=op.f("fk_section_printers_printer_id_printers"),
),
sa.ForeignKeyConstraint(
["section_id"],
["sections.id"],
name=op.f("fk_section_printers_section_id_sections"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_section_printers")),
sa.UniqueConstraint(
"menu_category_id",
"section_id",
name=op.f("uq_section_printers_menu_category_id"),
),
)
op.create_table(
"user_roles",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("role_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], name=op.f("fk_user_roles_role_id_roles")),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], name=op.f("fk_user_roles_user_id_users")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_user_roles")),
sa.UniqueConstraint("user_id", "role_id", name=op.f("uq_user_roles_user_id")),
)
op.create_table(
"products",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("name", sa.Unicode(length=255), nullable=False),
sa.Column("units", sa.Unicode(length=255), nullable=False),
sa.Column("menu_category_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("sale_category_id", postgresql.UUID(as_uuid=True), nullable=True),
sa.Column("sale_category_name", sa.Unicode(length=255), nullable=True),
sa.Column("price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("has_happy_hour", sa.Boolean(), nullable=False),
sa.Column("is_not_available", sa.Boolean(), nullable=False),
sa.Column("quantity", sa.Numeric(precision=15, scale=2), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["menu_category_id"],
["menu_categories.id"],
name=op.f("fk_products_menu_category_id_menu_categories"),
),
sa.ForeignKeyConstraint(
["sale_category_id"],
["sale_categories.id"],
name=op.f("fk_products_sale_category_id_sale_categories"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_products")),
sa.UniqueConstraint("name", "units", name=op.f("uq_products_name")),
)
op.create_table(
"vouchers",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("date", sa.DateTime(), nullable=False),
sa.Column("pax", sa.Integer(), nullable=False),
sa.Column("bill_id", sa.Integer(), nullable=True),
sa.Column("kot_id", sa.Integer(), nullable=False),
sa.Column("creation_date", sa.DateTime(), nullable=False),
sa.Column("last_edit_date", sa.DateTime(), nullable=False),
sa.Column("food_table_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("customer_id", postgresql.UUID(as_uuid=True), nullable=True),
sa.Column("narration", sa.Unicode(length=1000), nullable=True),
sa.Column("reason", sa.Unicode(length=255), nullable=True),
sa.Column("voucher_type", sa.Integer(), nullable=False),
sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(
["customer_id"],
["customers.id"],
name=op.f("fk_vouchers_customer_id_customers"),
),
sa.ForeignKeyConstraint(
["food_table_id"],
["food_tables.id"],
name=op.f("fk_vouchers_food_table_id_food_tables"),
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], name=op.f("fk_vouchers_user_id_users")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_vouchers")),
sa.UniqueConstraint("bill_id", "voucher_type", name=op.f("uq_vouchers_bill_id")),
sa.UniqueConstraint("kot_id", name=op.f("uq_vouchers_kot_id")),
)
op.create_index(op.f("ix_vouchers_date"), "vouchers", ["date"], unique=False)
op.create_table(
"kots",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("voucher_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("code", sa.Integer(), nullable=False),
sa.Column("food_table_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("date", sa.DateTime(), nullable=False),
sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(
["food_table_id"],
["food_tables.id"],
name=op.f("fk_kots_food_table_id_food_tables"),
),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], name=op.f("fk_kots_user_id_users")),
sa.ForeignKeyConstraint(["voucher_id"], ["vouchers.id"], name=op.f("fk_kots_voucher_id_vouchers")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_kots")),
sa.UniqueConstraint("code", name=op.f("uq_kots_code")),
)
op.create_index(op.f("ix_kots_date"), "kots", ["date"], unique=False)
op.create_index(op.f("ix_kots_voucher_id"), "kots", ["voucher_id"], unique=False)
op.create_table(
"modifier_categories_products",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("product_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("modifier_categories_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(
["modifier_categories_id"],
["modifier_categories.id"],
name=op.f("fk_modifier_categories_products_modifier_categories_id_modifier_categories"),
),
sa.ForeignKeyConstraint(
["product_id"],
["products.id"],
name=op.f("fk_modifier_categories_products_product_id_products"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_modifier_categories_products")),
sa.UniqueConstraint(
"product_id",
"modifier_categories_id",
name=op.f("uq_modifier_categories_products_product_id"),
),
)
op.create_table(
"overview",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("voucher_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("food_table_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("guest_book_id", postgresql.UUID(as_uuid=True), nullable=True),
sa.Column("status", sa.Unicode(length=255), nullable=False),
sa.ForeignKeyConstraint(
["food_table_id"],
["food_tables.id"],
name=op.f("fk_overview_food_table_id_food_tables"),
),
sa.ForeignKeyConstraint(
["guest_book_id"],
["guest_book.id"],
name=op.f("fk_overview_guest_book_id_guest_book"),
),
sa.ForeignKeyConstraint(
["voucher_id"],
["vouchers.id"],
name=op.f("fk_overview_voucher_id_vouchers"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_overview")),
sa.UniqueConstraint("food_table_id", name=op.f("uq_overview_food_table_id")),
sa.UniqueConstraint("guest_book_id", name=op.f("uq_overview_guest_book_id")),
sa.UniqueConstraint("voucher_id", name=op.f("uq_overview_voucher_id")),
)
op.create_table(
"reprints",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("date", sa.DateTime(), nullable=False),
sa.Column("voucher_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], name=op.f("fk_reprints_user_id_users")),
sa.ForeignKeyConstraint(
["voucher_id"],
["vouchers.id"],
name=op.f("fk_reprints_voucher_id_vouchers"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_reprints")),
)
op.create_index(op.f("ix_reprints_date"), "reprints", ["date"], unique=False)
op.create_index(op.f("ix_reprints_voucher_id"), "reprints", ["voucher_id"], unique=False)
op.create_table(
"settlements",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("voucher_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("settled", sa.Integer(), nullable=False),
sa.Column("amount", sa.Numeric(precision=15, scale=2), nullable=False),
sa.ForeignKeyConstraint(
["settled"],
["settle_options.id"],
name=op.f("fk_settlements_settled_settle_options"),
),
sa.ForeignKeyConstraint(
["voucher_id"],
["vouchers.id"],
name=op.f("fk_settlements_voucher_id_vouchers"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_settlements")),
sa.UniqueConstraint("voucher_id", "settled", name=op.f("uq_settlements_voucher_id")),
)
op.create_index(op.f("ix_settlements_voucher_id"), "settlements", ["voucher_id"], unique=False)
op.create_table(
"inventories",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("kot_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("product_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("quantity", sa.Numeric(precision=15, scale=2), nullable=True),
sa.Column("price", sa.Numeric(precision=15, scale=2), nullable=True),
sa.Column("is_happy_hour", sa.Boolean(), nullable=False),
sa.Column("tax_rate", sa.Numeric(precision=15, scale=5), nullable=True),
sa.Column("tax_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("discount", sa.Numeric(precision=15, scale=5), nullable=True),
sa.Column("sort_order", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(["kot_id"], ["kots.id"], name=op.f("fk_inventories_kot_id_kots")),
sa.ForeignKeyConstraint(
["product_id"],
["products.id"],
name=op.f("fk_inventories_product_id_products"),
),
sa.ForeignKeyConstraint(["tax_id"], ["taxes.id"], name=op.f("fk_inventories_tax_id_taxes")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_inventories")),
sa.UniqueConstraint(
"kot_id",
"product_id",
"is_happy_hour",
"price",
name=op.f("uq_inventories_kot_id"),
),
)
op.create_index(op.f("ix_inventories_kot_id"), "inventories", ["kot_id"], unique=False)
op.create_table(
"inventory_modifiers",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),