Compare commits

...
5 Commits
27 changed files with 199 additions and 74 deletions
+61
View File
@@ -0,0 +1,61 @@
# Version control & IDEs
.git
.gitignore
.vscode
.idea
.agents
.gemini
# Build artifacts & compiled outputs
frontend
dist
**/dist
out-tsc
**/out-tsc
bazel-out
# Dependencies & framework caches
node_modules
**/node_modules
.angular
**/.angular
.nx
**/.nx
.sass-cache
**/.sass-cache
# Python virtual environments & caches
.venv
**/__pycache__
**/.mypy_cache
**/.ruff_cache
**/.pytest_cache
*.py[cod]
*$py.class
*.egg-info
# Testing & coverage
coverage
**/coverage
.coverage
htmlcov
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment & local deployment/dev files
.env*
!.env.example
ansible
deploy.sh
lint.sh
version_bump.sh
Makefile
README.md
CHANGES.md
typings
.DS_Store
Thumbs.db
+47 -1
View File
@@ -1,5 +1,51 @@
# IDEs & System Files
.idea/
frontend
.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/
.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
+18 -1
View File
@@ -27,19 +27,36 @@
"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,
"/overlord/.angular/**": 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",
-1
View File
@@ -29,7 +29,6 @@ RUN \
else echo "Lockfile not found." && exit 1; \
fi
FROM python:3.14-slim AS runner
LABEL maintainer="Amritanshu <docker@tanshu.com>"
+1 -1
View File
@@ -1 +1 @@
__version__ = "15.2.3"
__version__ = "15.2.5"
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "brewman"
version = "15.2.3"
version = "15.2.5"
description = "Accounting plus inventory management for a restaurant."
requires-python = ">=3.14"
authors = [{ name = "tanshu", email = "git@tanshu.com" }]
+1 -1
View File
@@ -211,7 +211,7 @@ wheels = [
[[package]]
name = "brewman"
version = "15.2.3"
version = "15.2.5"
source = { editable = "." }
dependencies = [
{ name = "alembic" },
+6 -6
View File
@@ -7,10 +7,10 @@ parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P )
cd "$parent_path/overlord" || exit
npx prettier --write src/app
npx ng lint --fix
cd "$parent_path/brewman" || exit
uvx ruff format .
uvx ruff check . # Lint all files in the current directory (and any subdirectories)
uvx bandit --recursive brewman
uvx bandit --recursive .
uvx safety check
cd "$parent_path/brewman" || exit
uv run ruff format .
uv run ruff check . # Lint all files in the current directory (and any subdirectories)
uv run bandit --recursive brewman
uv run bandit --recursive .
uv run safety check
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "overlord",
"version": "15.2.3",
"version": "15.2.5",
"scripts": {
"ng": "ng",
"start": "ng serve",
+1 -1
View File
@@ -1,6 +1,6 @@
export const environment = {
ACCESS_TOKEN_REFRESH_MINUTES: 10, // refresh token 10 minutes before expiry
version: '15.2.3',
version: '15.2.5',
title: 'HnG / TGB',
};
+2 -2
View File
@@ -134,9 +134,9 @@
<span class="flex">
Created on&nbsp;<strong>{{ form.creationDate().value() | localTime }}</strong
>&nbsp;and Last Edited on&nbsp; <strong>{{ form.lastEditDate().value() | localTime }}</strong
>&nbsp;by&nbsp; <strong>{{ form.userName() }}</strong
>&nbsp;by&nbsp;<strong>{{ form.userName().value().name }}</strong
>.
{{ form.posted() ? 'Posted by ' + form.poster() : '' }}
{{ form.posted().value() ? 'Posted by ' + form.poster().value() : '' }}
</span>
}
</div>
+2 -2
View File
@@ -58,7 +58,7 @@ export interface IssueFormModel {
posted: boolean;
creationDate: string;
lastEditDate: string;
userName: string;
userName: User;
poster: string;
}
@@ -139,7 +139,7 @@ export class IssueComponent {
posted: voucher.posted,
creationDate: voucher.creationDate,
lastEditDate: voucher.lastEditDate,
userName: voucher.user?.name ?? '',
userName: voucher.user ?? new User(),
poster: voucher.poster ?? '',
};
},
@@ -174,8 +174,8 @@
<span class="flex">
Created on&nbsp;<strong>{{ form.creationDate().value() | localTime }}</strong
>&nbsp;and Last Edited on&nbsp; <strong>{{ form.lastEditDate().value() | localTime }}</strong
>&nbsp;by&nbsp; <strong>{{ form.userName() }}</strong
>. {{ form.posted() ? 'Posted by ' + form.poster() : '' }}
>&nbsp;by&nbsp;<strong>{{ form.userName().value().name }}</strong
>. {{ form.posted().value() ? 'Posted by ' + form.poster().value() : '' }}
</span>
}
</div>
@@ -26,6 +26,7 @@ import { AccountService } from '../core/account.service';
import { CostCentre } from '../core/cost-centre';
import { DbFile } from '../core/db-file';
import { Journal } from '../core/journal';
import { User } from '../core/user';
import { Voucher } from '../core/voucher';
import { VoucherService } from '../core/voucher.service';
import { AccountingPipe } from '../shared/accounting.pipe';
@@ -64,7 +65,7 @@ export interface JournalFormModel {
posted: boolean;
creationDate: string;
lastEditDate: string;
userName: string;
userName: User;
poster: string;
}
@@ -194,7 +195,7 @@ export class JournalComponent {
posted: voucher.posted,
creationDate: voucher.creationDate,
lastEditDate: voucher.lastEditDate,
userName: voucher.user?.name ?? '',
userName: voucher.user ?? new User(),
poster: voucher.poster,
}),
});
@@ -182,9 +182,9 @@
<span class="flex">
Created on&nbsp;<strong>{{ form.creationDate().value() | localTime }}</strong
>&nbsp;and Last Edited on&nbsp; <strong>{{ form.lastEditDate().value() | localTime }}</strong
>&nbsp;by&nbsp; <strong>{{ form.userName() }}</strong
>&nbsp;by&nbsp;<strong>{{ form.userName().value().name }}</strong
>.
{{ form.posted() ? 'Posted by ' + form.poster() : '' }}
{{ form.posted().value() ? 'Posted by ' + form.poster().value() : '' }}
</span>
}
</div>
@@ -65,7 +65,7 @@ export interface PaymentFormModel {
posted: boolean;
creationDate: string;
lastEditDate: string;
userName: string;
userName: User;
poster: string;
}
@@ -153,7 +153,7 @@ export class PaymentComponent {
posted: voucher.posted,
creationDate: voucher.creationDate,
lastEditDate: voucher.lastEditDate,
userName: voucher.user?.name ?? '',
userName: voucher.user ?? new User(),
poster: voucher.poster ?? '',
};
},
@@ -207,9 +207,9 @@
<span class="flex">
Created on&nbsp;<strong>{{ form.creationDate().value() | localTime }}</strong
>&nbsp;and Last Edited on&nbsp; <strong>{{ form.lastEditDate().value() | localTime }}</strong
>&nbsp;by&nbsp; <strong>{{ form.userName() }}</strong
>&nbsp;by&nbsp;<strong>{{ form.userName().value().name }}</strong
>.
{{ form.posted() ? 'Posted by ' + form.poster() : '' }}
{{ form.posted().value() ? 'Posted by ' + form.poster().value() : '' }}
</span>
}
</div>
@@ -68,7 +68,7 @@ export interface PurchaseReturnFormModel {
posted: boolean;
creationDate: string;
lastEditDate: string;
userName: string;
userName: User;
poster: string;
}
@@ -157,7 +157,7 @@ export class PurchaseReturnComponent {
posted: voucher.posted,
creationDate: voucher.creationDate,
lastEditDate: voucher.lastEditDate,
userName: voucher.user?.name ?? '',
userName: voucher.user ?? null,
poster: voucher.poster ?? '',
};
},
@@ -227,9 +227,9 @@
<span class="flex">
Created on&nbsp;<strong>{{ form.creationDate().value() | localTime }}</strong
>&nbsp;and Last Edited on&nbsp; <strong>{{ form.lastEditDate().value() | localTime }}</strong
>&nbsp;by&nbsp; <strong>{{ form.userName() }}</strong
>&nbsp;by&nbsp;<strong>{{ form.userName().value().name }}</strong
>.
{{ form.posted() ? 'Posted by ' + form.poster() : '' }}
{{ form.posted().value() ? 'Posted by ' + form.poster().value() : '' }}
</span>
}
</div>
@@ -73,7 +73,7 @@ export interface PurchaseFormModel {
posted: boolean;
creationDate: string;
lastEditDate: string;
userName: string;
userName: User;
poster: string;
}
@@ -165,7 +165,7 @@ export class PurchaseComponent {
posted: voucher.posted,
creationDate: voucher.creationDate,
lastEditDate: voucher.lastEditDate,
userName: voucher.user?.name ?? '',
userName: voucher.user ?? null,
poster: voucher.poster ?? '',
};
},
@@ -177,8 +177,8 @@
<span class="flex">
Created on&nbsp;<strong>{{ form.creationDate().value() | localTime }}</strong
>&nbsp;and Last Edited on&nbsp; <strong>{{ form.lastEditDate().value() | localTime }}</strong
>&nbsp;by&nbsp; <strong>{{ form.userName() }}</strong
>. {{ form.posted() ? 'Posted by ' + form.poster() : '' }}
>&nbsp;by&nbsp;<strong>{{ form.userName().value().name }}</strong
>. {{ form.posted().value() ? 'Posted by ' + form.poster().value() : '' }}
</span>
}
</div>
@@ -65,7 +65,7 @@ export interface ReceiptFormModel {
posted: boolean;
creationDate: string;
lastEditDate: string;
userName: string;
userName: User;
poster: string;
}
@@ -155,7 +155,7 @@ export class ReceiptComponent {
posted: voucher.posted,
creationDate: voucher.creationDate,
lastEditDate: voucher.lastEditDate,
userName: voucher.user?.name ?? '',
userName: voucher.user ?? null,
poster: voucher.poster ?? '',
};
},
@@ -16,16 +16,16 @@
<div class="row-container">
<mat-form-field class="flex-auto">
<mat-select [formField]="form.period">
@for (p of periods(); track p.id) {
<mat-option [value]="p"> {{ p.validFrom }} to {{ p.validTill }} </mat-option>
@for (p of periods(); track p) {
<mat-option [value]="p.id"> {{ p.validFrom }} to {{ p.validTill }} </mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field class="flex-auto">
<mat-label>Product Type</mat-label>
<mat-select [formField]="form.productGroup" (selectionChange)="filterProductGroup($event.value)">
<mat-option>-- All Products --</mat-option>
@for (mc of productGroups(); track mc.id) {
<mat-select [formField]="form.productGroup">
<mat-option [value]="null">-- All Products --</mat-option>
@for (mc of productGroups(); track mc) {
<mat-option [value]="mc.id">
{{ mc.name }}
</mat-option>
@@ -1,4 +1,4 @@
import { Component, inject, input, computed, effect, signal } from '@angular/core';
import { Component, inject, input, computed, effect, signal, linkedSignal } from '@angular/core';
import { form as createForm, FormField, FormRoot } from '@angular/forms/signals';
import { MatButtonModule } from '@angular/material/button';
import { MatOptionModule } from '@angular/material/core';
@@ -11,7 +11,6 @@ import { MatSortModule, Sort } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { Router, RouterModule } from '@angular/router';
import { ProductGroup } from '../../core/product-group';
import { Period } from '../../period/period';
import { PeriodService } from '../../period/period.service';
import { ProductGroupService } from '../../product-group/product-group.service';
@@ -19,6 +18,10 @@ import { ErrorStateComponent } from '../../shared/error-state/error-state.compon
import { SkeletonLoaderComponent } from '../../shared/skeleton-loader/skeleton-loader.component';
import { RecipeService } from '../recipe.service';
export interface RecipeListFormData {
period: string | null;
productGroup: string | null;
}
@Component({
selector: 'app-recipe-list',
templateUrl: './recipe-list.component.html',
@@ -52,31 +55,34 @@ export class RecipeListComponent {
sortActive = signal('');
sortDirection = signal('');
p = input<string>();
model = signal({
period: null as Period | null,
productGroup: null as ProductGroup | string | null,
});
form = createForm(this.model);
p = input('', { transform: (v: string | null | undefined) => v ?? null });
periodsResource = this.periodSer.list();
periods = computed(() => this.periodsResource.value() ?? []);
model = linkedSignal<string | null, RecipeListFormData>({
source: () => this.p(),
computation: (period) => ({
period: period,
productGroup: null,
}),
});
form = createForm(this.model);
productGroupsResource = this.productGroupSer.list();
productGroups = computed(() => this.productGroupsResource.value() ?? []);
periodFilter = computed(() => this.p() || '');
resource = this.ser.list(this.periodFilter);
resource = this.ser.list(this.p);
productGroupFilter = computed(() => this.model().productGroup ?? null);
info = computed(() => this.resource.value() ?? []);
sortedList = computed(() => {
let data = this.info() ?? [];
const pg = this.model().productGroup;
const pg = this.productGroupFilter();
if (pg) {
const pgId = typeof pg === 'string' ? pg : pg.id;
data = data.filter((r) => r.productGroupId === pgId);
data = data.filter((r) => r.productGroupId === pg);
}
const active = this.sortActive();
@@ -118,18 +124,13 @@ export class RecipeListComponent {
const x = this.model().period;
if (!x) return;
this.router.navigate([], {
queryParams: { p: x.id },
queryParams: { p: x },
replaceUrl: true,
queryParamsHandling: 'merge',
});
this.period = x;
});
}
filterProductGroup(val: string) {
this.model.update((m) => ({ ...m, productGroup: val }));
}
handlePageEvent(e: PageEvent) {
this.pageSize.set(e.pageSize);
this.pageIndex.set(e.pageIndex);
@@ -8,7 +8,7 @@
<mat-form-field class="flex-auto basis-30">
<mat-label>Product Group</mat-label>
<mat-select [formField]="form.productGroup">
<mat-option>-- All Products --</mat-option>
<mat-option [value]="null">-- All Products --</mat-option>
@for (pg of productGroups(); track pg) {
<mat-option [value]="pg.id">
{{ pg.name }}
@@ -9,7 +9,6 @@ import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { Product, StockKeepingUnit } from '../../core/product';
import { ProductGroupService } from '../../product-group/product-group.service';
import { ErrorStateComponent } from '../../shared/error-state/error-state.component';
import { SkeletonLoaderComponent } from '../../shared/skeleton-loader/skeleton-loader.component';
@@ -18,7 +17,7 @@ import { TemporalProductService } from '../temporal-product.service';
export interface TemporalProductListFormData {
filter: string;
productGroup: Product | StockKeepingUnit | string | null;
productGroup: string | null;
}
@Component({
@@ -66,16 +65,11 @@ export class TemporalProductListComponent {
form = createForm(this.formModel);
filterSignal = computed(() => this.formModel().filter);
debouncedFilter = debounced(this.filterSignal, 150);
productGroupFilter = computed(() => this.formModel().productGroup ?? null);
filteredList = computed(() => {
const data = this.list();
const search = this.debouncedFilter.value() ?? '';
const productGroup = this.formModel().productGroup;
const groupId =
typeof productGroup === 'string'
? productGroup
: productGroup && 'id' in productGroup
? (productGroup.id ?? '')
: '';
const productGroup = this.productGroupFilter();
const tokens = search.trim().toLowerCase().split(/\s+/).filter(Boolean);
@@ -97,7 +91,7 @@ export class TemporalProductListComponent {
}),
);
const matchesProductGroup = !groupId || products.some((k) => (k.productGroup?.id ?? '') === groupId);
const matchesProductGroup = !productGroup || products.some((k) => k.productGroup?.id === productGroup);
return matchesSearch && matchesProductGroup;
});
});
+6
View File
@@ -3,6 +3,12 @@ set -euo pipefail
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P )
cd "$parent_path" || exit
last_commit_subject=$(git log -1 --format=%s)
if [[ "$last_commit_subject" =~ ^Version\ Bump ]]; then
echo "Last commit is already a version bump (\"$last_commit_subject\"). Nothing to do."
exit 0
fi
if [ 1 -eq "$#" ]
then
current_version=$(grep -m 1 '^version = ' brewman/pyproject.toml | sed -E 's/version = "([^"]+)"/\1/')