Compare commits
499
Commits
v1.0.0-alpha.1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dfbfd8fcb | ||
|
|
221fadfdfc | ||
|
|
11b93cb11c | ||
|
|
302a4eb311 | ||
|
|
217f636573 | ||
|
|
95ac73bbc4 | ||
|
|
3d86f8706f | ||
|
|
30cbb60efe | ||
|
|
227f5b5f74 | ||
|
|
50974c7574 | ||
|
|
c8e1ab74b6 | ||
|
|
85ba7b326a | ||
|
|
458dfaad50 | ||
|
|
0a9675f270 | ||
|
|
6cf42d3641 | ||
|
|
cf680cd254 | ||
|
|
80d21ec31e | ||
|
|
4377016cda | ||
|
|
67209886b9 | ||
|
|
4e1c0d1ec7 | ||
|
|
c7412f080d | ||
|
|
d023089720 | ||
|
|
dd02a97ba2 | ||
|
|
befd4eb298 | ||
|
|
d90c1cb563 | ||
|
|
e90c80999e | ||
|
|
42331b482d | ||
|
|
612e2243c0 | ||
|
|
35a3bb9ad2 | ||
|
|
ae69c2fafe | ||
|
|
85a063c1a7 | ||
|
|
10448e8b12 | ||
|
|
59bf2c5ca9 | ||
|
|
73fba17711 | ||
|
|
103983846d | ||
|
|
af07bb5043 | ||
|
|
612426b92f | ||
|
|
730048f239 | ||
|
|
ff537edb06 | ||
|
|
311464e593 | ||
|
|
43204e8441 | ||
|
|
2a29dc9195 | ||
|
|
65527dcbff | ||
|
|
cc52670783 | ||
|
|
993f83c786 | ||
|
|
59eb45da96 | ||
|
|
ebe5a6352e | ||
|
|
38a8656008 | ||
|
|
2e5543a2eb | ||
|
|
34e30efcaf | ||
|
|
2b844bc44b | ||
|
|
325bcae998 | ||
|
|
ab8fed17d0 | ||
|
|
dcf1ffad98 | ||
|
|
338d9d63d5 | ||
|
|
9b0da9cb65 | ||
|
|
4cc2ff2229 | ||
|
|
c9fa83e8ac | ||
|
|
061dd1310d | ||
|
|
a1d6919d9d | ||
|
|
ecc92795e6 | ||
|
|
535a145742 | ||
|
|
1271e26dd8 | ||
|
|
2ae3f3ba7e | ||
|
|
e6a03d33e5 | ||
|
|
c7dd6e574d | ||
|
|
913820cc29 | ||
|
|
e3d40d50f0 | ||
|
|
b27a5a9211 | ||
|
|
a955e26b93 | ||
|
|
bd2b1f5c2f | ||
|
|
0fa28019ec | ||
|
|
ddb03ac8eb | ||
|
|
ae8975b29f | ||
|
|
e7e49019c4 | ||
|
|
dc34a78e06 | ||
|
|
91b3740ec0 | ||
|
|
7382d487ac | ||
|
|
8957e4ce8c | ||
|
|
6e8843d3c4 | ||
|
|
b79af6e1c6 | ||
|
|
c65517b377 | ||
|
|
ca94f8f57e | ||
|
|
e0766c5a2e | ||
|
|
bd1a5e03c5 | ||
|
|
61f5f2f1af | ||
|
|
0a7ffb4a5c | ||
|
|
22f888500f | ||
|
|
2505ef7aec | ||
|
|
4f0346551a | ||
|
|
e4072d0872 | ||
|
|
1b8ab2857f | ||
|
|
aaf64ab75a | ||
|
|
6caeb34e8d | ||
|
|
88b53b5781 | ||
|
|
8ff7363fc8 | ||
|
|
e1cbf7ffb8 | ||
|
|
b33468a43f | ||
|
|
3e8f2e9736 | ||
|
|
f2d5d62aa6 | ||
|
|
eee9ae1f11 | ||
|
|
2fbfe96c40 | ||
|
|
01b0cbfadf | ||
|
|
88a1572747 | ||
|
|
5c1c474e93 | ||
|
|
74e17dffd0 | ||
|
|
8ad9e6562c | ||
|
|
d80d097c0e | ||
|
|
0dbec4784b | ||
|
|
af684c976e | ||
|
|
26c65b4b65 | ||
|
|
2c89e218c8 | ||
|
|
b2732434f4 | ||
|
|
5317e3de23 | ||
|
|
add3ad35d7 | ||
|
|
feb867fe28 | ||
|
|
fc2ef36a0c | ||
|
|
31b4ee1ebf | ||
|
|
0e9d9c5934 | ||
|
|
5401318fc1 | ||
|
|
66834b7399 | ||
|
|
44513dd6be | ||
|
|
86722e3558 | ||
|
|
2ca94e5bea | ||
|
|
ad8ccb26df | ||
|
|
abc1b36721 | ||
|
|
68ab90ec48 | ||
|
|
7be7545137 | ||
|
|
b46fd10f5a | ||
|
|
50846ace3f | ||
|
|
e36de39ed5 | ||
|
|
4b1fca0260 | ||
|
|
43a884144b | ||
|
|
85a939b761 | ||
|
|
3bfa00364c | ||
|
|
0b092b5467 | ||
|
|
82747c4b89 | ||
|
|
e732354671 | ||
|
|
6d3057bb55 | ||
|
|
15a93c599e | ||
|
|
823e3a761e | ||
|
|
ac728128b9 | ||
|
|
5536bcae41 | ||
|
|
55d5017254 | ||
|
|
bbb7be8070 | ||
|
|
b1407f339d | ||
|
|
659fed549a | ||
|
|
a5efcc9061 | ||
|
|
7e70214c94 | ||
|
|
2495c24e1a | ||
|
|
010e9a84db | ||
|
|
56c1be5e05 | ||
|
|
cbd5da12cc | ||
|
|
93b1b8840d | ||
|
|
ecea277e46 | ||
|
|
1cb2677ad1 | ||
|
|
e8a33cd80e | ||
|
|
cb3657615a | ||
|
|
e955ef4196 | ||
|
|
f1ef578eae | ||
|
|
05302c4291 | ||
|
|
96e54e2a0a | ||
|
|
26310dfc42 | ||
|
|
16f29c4139 | ||
|
|
f8c1e0591b | ||
|
|
42102d7fef | ||
|
|
09b762d154 | ||
|
|
8b2d15b9e1 | ||
|
|
5534b3983d | ||
|
|
e685941804 | ||
|
|
8bc7d66123 | ||
|
|
302ed4a18f | ||
|
|
2fb2e01ca1 | ||
|
|
94cc8ccd47 | ||
|
|
c13047e812 | ||
|
|
5c697dd43a | ||
|
|
56ae7500cc | ||
|
|
1b4c26733d | ||
|
|
88bda14002 | ||
|
|
7a8017087b | ||
|
|
1297a4e7d2 | ||
|
|
47afb7e29c | ||
|
|
4db26722a3 | ||
|
|
b2db7e8f9c | ||
|
|
f6deea265c | ||
|
|
f1d145e76f | ||
|
|
22f61427b0 | ||
|
|
f9fc6dba0d | ||
|
|
e56a753053 | ||
|
|
03918fee80 | ||
|
|
c14e64ce85 | ||
|
|
4e4cebf5d4 | ||
|
|
d39712a347 | ||
|
|
40a357edc8 | ||
|
|
1a08066c2d | ||
|
|
a62018ad1f | ||
|
|
da46591313 | ||
|
|
a1ccf973cd | ||
|
|
65700d2760 | ||
|
|
77c7b167ef | ||
|
|
efa2af396d | ||
|
|
b021861ba3 | ||
|
|
245937ecf2 | ||
|
|
3af0b0ac96 | ||
|
|
4fffdd85d6 | ||
|
|
7da9212cf7 | ||
|
|
0f1660ef5a | ||
|
|
7d0874093c | ||
|
|
516b22ed1c | ||
|
|
c5eaeb1497 | ||
|
|
f408e84af3 | ||
|
|
e46fe7f90e | ||
|
|
802eded568 | ||
|
|
5c7985e392 | ||
|
|
792ccf923f | ||
|
|
f637f01954 | ||
|
|
236c746e1c | ||
|
|
a4db3b24f0 | ||
|
|
9f4ec3697c | ||
|
|
9fe84800a7 | ||
|
|
f73de53d21 | ||
|
|
e37d160b7c | ||
|
|
61bb62475b | ||
|
|
2f1f9eee1f | ||
|
|
f9124abb6c | ||
|
|
09a8fdfecc | ||
|
|
7a2ccb7fb1 | ||
|
|
1eccd7f9d5 | ||
|
|
a213c1a0b5 | ||
|
|
1a8992c296 | ||
|
|
676719917a | ||
|
|
1071b57484 | ||
|
|
3eaf4cd41d | ||
|
|
c494284517 | ||
|
|
34ced063a1 | ||
|
|
59f47e046b | ||
|
|
54c797d1a8 | ||
|
|
d43aab0b23 | ||
|
|
1f1a9a13cd | ||
|
|
d9d5754e26 | ||
|
|
a174f6b847 | ||
|
|
1a4e51051c | ||
|
|
ee7a24fa77 | ||
|
|
ef9e28c95b | ||
|
|
0837aebfb8 | ||
|
|
124cf4d9ff | ||
|
|
debe0df7b7 | ||
|
|
5ff954deb6 | ||
|
|
b70a296d8b | ||
|
|
6379e5f4e3 | ||
|
|
c478290da0 | ||
|
|
5bea488181 | ||
|
|
63dfe05044 | ||
|
|
4a12ee0834 | ||
|
|
f929a731cb | ||
|
|
1515c33e7b | ||
|
|
beae4a07c6 | ||
|
|
1ef7f98a07 | ||
|
|
70c9d22bbc | ||
|
|
baefaeb228 | ||
|
|
5e0e99340e | ||
|
|
5402d229f8 | ||
|
|
2f440a4127 | ||
|
|
0b3ec5da16 | ||
|
|
afa4fd4711 | ||
|
|
eedc571ade | ||
|
|
5cd0acc7a9 | ||
|
|
c2483ae321 | ||
|
|
8c5d941850 | ||
|
|
38ccaa167c | ||
|
|
997e8e2dd0 | ||
|
|
c73b812911 | ||
|
|
5ece52ad55 | ||
|
|
17702a433b | ||
|
|
1287d8f7ac | ||
|
|
0d41c0e345 | ||
|
|
fd2659eee5 | ||
|
|
db5f2731be | ||
|
|
143ddfb860 | ||
|
|
7247ac34f4 | ||
|
|
a1a1c10dff | ||
|
|
eb2f783b9e | ||
|
|
da9d58025c | ||
|
|
77dad64eab | ||
|
|
e0ef5e7b64 | ||
|
|
ca3a599386 | ||
|
|
8a243fe387 | ||
|
|
40aced4fa3 | ||
|
|
567fb836b3 | ||
|
|
1c222872d2 | ||
|
|
5d5439ae3b | ||
|
|
4530a183c9 | ||
|
|
7795f5ebef | ||
|
|
15cd40d779 | ||
|
|
27205f7790 | ||
|
|
72b0e0e97d | ||
|
|
6e284de75b | ||
|
|
625a484bcd | ||
|
|
41431829f0 | ||
|
|
b47715bc22 | ||
|
|
70d76f9951 | ||
|
|
d14250c0c7 | ||
|
|
7cf6775ea1 | ||
|
|
d9a6c5d20f | ||
|
|
83aa47b33a | ||
|
|
66e541d50b | ||
|
|
9961f2c3a1 | ||
|
|
9f5a0b1209 | ||
|
|
1f516e75e6 | ||
|
|
375e605e12 | ||
|
|
defba7636c | ||
|
|
12a361aff1 | ||
|
|
a4d389bba5 | ||
|
|
e1b824dce5 | ||
|
|
f78ad68235 | ||
|
|
59bab991fd | ||
|
|
2f0ef397cf | ||
|
|
87402871a5 | ||
|
|
f410d4a69e | ||
|
|
31e9071713 | ||
|
|
74e67a98e6 | ||
|
|
e144175fe0 | ||
|
|
e66716710a | ||
|
|
76b27417d4 | ||
|
|
a29e9b8789 | ||
|
|
2b22d6c4b5 | ||
|
|
35a431d37e | ||
|
|
581d9ca507 | ||
|
|
e753ee625f | ||
|
|
12f63cec73 | ||
|
|
2612da063b | ||
|
|
438e99b6b0 | ||
|
|
862892d7a1 | ||
|
|
d92cd295e1 | ||
|
|
0477fde460 | ||
|
|
9c0936e772 | ||
|
|
ac0b8d2565 | ||
|
|
4e827c2bd0 | ||
|
|
4448869652 | ||
|
|
978f0fcdff | ||
|
|
c0a4956fd4 | ||
|
|
b9c3b7eda5 | ||
|
|
00730c1463 | ||
|
|
32c74c65fc | ||
|
|
07d13898c3 | ||
|
|
823808d402 | ||
|
|
54d205eebe | ||
|
|
e1ed264674 | ||
|
|
91451eebc9 | ||
|
|
78dcd6b92a | ||
|
|
4c867138e2 | ||
|
|
c3df28eb64 | ||
|
|
73850560aa | ||
|
|
faf83f9356 | ||
|
|
31ae068cab | ||
|
|
97579ea9d3 | ||
|
|
0da16e9548 | ||
|
|
214c40fbde | ||
|
|
d5b4dfeaca | ||
|
|
3705ceb95b | ||
|
|
0046c4fdd1 | ||
|
|
47381527c2 | ||
|
|
095f4fe473 | ||
|
|
7ff546a442 | ||
|
|
11600de3a8 | ||
|
|
f4caa19bb2 | ||
|
|
0b30ce258c | ||
|
|
669821a643 | ||
|
|
0775d12271 | ||
|
|
c29fde0cb0 | ||
|
|
dc6e7fe467 | ||
|
|
592f26af20 | ||
|
|
e99b8f3070 | ||
|
|
e8d178a248 | ||
|
|
088982c6d4 | ||
|
|
52943c7ee0 | ||
|
|
1163989866 | ||
|
|
30685e10a8 | ||
|
|
2374fd7347 | ||
|
|
4257db020d | ||
|
|
d851714136 | ||
|
|
ea2bfb1c0c | ||
|
|
716c74c974 | ||
|
|
41d4ef1200 | ||
|
|
6257313792 | ||
|
|
161896154d | ||
|
|
98c75f66c9 | ||
|
|
7b327b4fda | ||
|
|
565cffaef1 | ||
|
|
7550ced477 | ||
|
|
b85e930ace | ||
|
|
c6da3b9e38 | ||
|
|
4386ee45b8 | ||
|
|
f65c19ce1a | ||
|
|
9b55d037f2 | ||
|
|
4e3362c614 | ||
|
|
f4b85dcb41 | ||
|
|
89c93862bf | ||
|
|
7bd46a946e | ||
|
|
8bb6235e67 | ||
|
|
36915b41c1 | ||
|
|
b70b01b726 | ||
|
|
f28cf1eea0 | ||
|
|
608dde4619 | ||
|
|
63f5871350 | ||
|
|
6c83c74424 | ||
|
|
28952402aa | ||
|
|
02e208cd4e | ||
|
|
e4500f0d46 | ||
|
|
e229ecefa2 | ||
|
|
22de4f7624 | ||
|
|
5a73387404 | ||
|
|
4f5ba47d46 | ||
|
|
294f53147f | ||
|
|
afc58d566c | ||
|
|
3ad73dff1c | ||
|
|
27aa4d12a6 | ||
|
|
d65379a068 | ||
|
|
130d9197e5 | ||
|
|
e16e1822b8 | ||
|
|
d9e0397b21 | ||
|
|
b583b90756 | ||
|
|
84535ca9bb | ||
|
|
f7b4b29d3a | ||
|
|
e8ae39cd7a | ||
|
|
3f49bea039 | ||
|
|
6567f560ab | ||
|
|
cabd6f2ea1 | ||
|
|
88f5b26948 | ||
|
|
163b40e9e5 | ||
|
|
aead1f1b36 | ||
|
|
2dd9f7d962 | ||
|
|
ef1ee35b65 | ||
|
|
67cb1e3d80 | ||
|
|
9317150bde | ||
|
|
5e180f48d5 | ||
|
|
d5b2da4388 | ||
|
|
6f9394dc8d | ||
|
|
8db9a7d757 | ||
|
|
77fb8e8428 | ||
|
|
de1b199da0 | ||
|
|
de235f3d9e | ||
|
|
a92726f5e6 | ||
|
|
4aaa3fc72b | ||
|
|
94b63e4385 | ||
|
|
0c9ebd29d7 | ||
|
|
9ea143d233 | ||
|
|
4108040cdd | ||
|
|
969b774a25 | ||
|
|
f1a360a4ea | ||
|
|
1e69c8eeeb | ||
|
|
f8683cf080 | ||
|
|
cbc2f29e29 | ||
|
|
00fe2410b7 | ||
|
|
1d6baa9d1d | ||
|
|
fc6eebed19 | ||
|
|
99f5f09c17 | ||
|
|
b7f382cac8 | ||
|
|
73f83f1aa7 | ||
|
|
7baf79c3fa | ||
|
|
9ecf0b5cc9 | ||
|
|
d677cfb1ea | ||
|
|
b31db593c2 | ||
|
|
066d43a373 | ||
|
|
3f2a06c2cf | ||
|
|
aaac5efea2 | ||
|
|
046504e097 | ||
|
|
f8778fee74 | ||
|
|
cf34c2b855 | ||
|
|
bf09471e9e | ||
|
|
578385f866 | ||
|
|
6c06271315 | ||
|
|
b19d8cc030 | ||
|
|
cabdd505e0 | ||
|
|
7b6c09cea3 | ||
|
|
938bb67e0a | ||
|
|
fdfd3dcbfb | ||
|
|
0c0a2990a8 | ||
|
|
a12f093828 | ||
|
|
6d0f30503a | ||
|
|
05860fb0b9 | ||
|
|
04fb6dfcab | ||
|
|
ee54e36dda | ||
|
|
461db9d8fa | ||
|
|
2c0c91b136 | ||
|
|
992a6b6767 | ||
|
|
5603c5d8fb | ||
|
|
fd1111f378 | ||
|
|
b898e26dc6 | ||
|
|
2b04b624b3 | ||
|
|
8f6c5930ee | ||
|
|
243c3cb280 | ||
|
|
0f00d96dc9 | ||
|
|
241568622e | ||
|
|
abc29844d3 | ||
|
|
70d31fea5e | ||
|
|
c6b907061e | ||
|
|
63c7a541dc | ||
|
|
aa81743ce7 |
No files matched your search
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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`.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`).
|
||||
@@ -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
|
||||
@@ -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
@@ -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/
|
||||
@@ -0,0 +1 @@
|
||||
3.14
|
||||
Vendored
+14
@@ -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"
|
||||
]
|
||||
}
|
||||
Vendored
+43
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
// For more information, visit: https://angular.dev/ai/mcp
|
||||
"servers": {
|
||||
"angular-cli": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@angular/cli", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+79
@@ -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": []
|
||||
}
|
||||
Vendored
+48
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File renamed without changes.
@@ -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
@@ -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
@@ -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"]
|
||||
@@ -1,2 +0,0 @@
|
||||
include *.txt *.ini *.cfg *.rst
|
||||
recursive-include barker
|
||||
@@ -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 \
|
||||
-
|
||||
@@ -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
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
app/package.json
|
||||
app/pyproject.toml
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
{{ host }} {
|
||||
reverse_proxy {{ host_directory }}:80
|
||||
}
|
||||
@@ -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
|
||||
Executable
+20
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
collections:
|
||||
- name: community.docker
|
||||
@@ -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 }}"
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
caddy_container: caddy
|
||||
caddy_caddyfile_path: /var/lib/caddy/conf/Caddyfile
|
||||
# snippet_block: |
|
||||
# {{ host }} {
|
||||
# reverse_proxy {{ barker_container }}:80
|
||||
# }
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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 }}"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
*/__pycache__/
|
||||
*.egg-info/
|
||||
@@ -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
|
||||
Vendored
+22
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
Generic single-database configuration.
|
||||
@@ -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()
|
||||
@@ -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),
|
||||