Feature: Recording the nutritional and ice cream related values in the database

This commit is contained in:
Amritanshu Agrawal 2023-12-25 10:57:44 +05:30
parent efaaf9d431
commit cd6a5e129f
14 changed files with 391 additions and 8 deletions

View File

@ -0,0 +1,77 @@
"""nut
Revision ID: 2438cd581f00
Revises: ba0fff092981
Create Date: 2023-10-16 16:26:45.922654
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "2438cd581f00"
down_revision = "ba0fff092981"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"products", sa.Column("protein", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("carbohydrate", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("total_sugar", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("added_sugar", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("total_fat", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("saturated_fat", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("trans_fat", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("cholestrol", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("sodium", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column("products", sa.Column("msnf", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False))
op.add_column(
"products", sa.Column("other_solids", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column(
"products", sa.Column("total_solids", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False)
)
op.add_column("products", sa.Column("water", sa.Numeric(precision=15, scale=5), server_default="0", nullable=False))
op.add_column("product_groups", sa.Column("nutritional", sa.Boolean(), server_default=False, nullable=False))
op.add_column("product_groups", sa.Column("ice_cream", sa.Boolean(), server_default=False, nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("products", "water")
op.drop_column("products", "total_solids")
op.drop_column("products", "other_solids")
op.drop_column("products", "msnf")
op.drop_column("products", "sodium")
op.drop_column("products", "cholestrol")
op.drop_column("products", "trans_fat")
op.drop_column("products", "saturated_fat")
op.drop_column("products", "total_fat")
op.drop_column("products", "added_sugar")
op.drop_column("products", "total_sugar")
op.drop_column("products", "carbohydrate")
op.drop_column("products", "protein")
# ### end Alembic commands ###

View File

@ -1,8 +1,9 @@
import uuid
from decimal import Decimal
from typing import TYPE_CHECKING
from sqlalchemy import Boolean, ForeignKey, Integer, Text, Unicode, Uuid
from sqlalchemy import Boolean, ForeignKey, Integer, Numeric, Text, Unicode, Uuid
from sqlalchemy.orm import Mapped, mapped_column, relationship
from ..db.base_class import reg
@ -38,6 +39,20 @@ class Product:
product_group: Mapped["ProductGroup"] = relationship("ProductGroup", back_populates="products")
account: Mapped["Account"] = relationship("Account", back_populates="products")
protein: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
carbohydrate: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
total_sugar: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
added_sugar: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
total_fat: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
saturated_fat: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
trans_fat: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
cholestrol: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
sodium: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
msnf: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
other_solids: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
total_solids: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
water: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False)
def __init__(
self,
name: str,
@ -47,6 +62,19 @@ class Product:
is_active: bool,
is_purchased: bool,
is_sold: bool,
protein: Decimal = 0,
carbohydrate: Decimal = 0,
total_sugar: Decimal = 0,
added_sugar: Decimal = 0,
total_fat: Decimal = 0,
saturated_fat: Decimal = 0,
trans_fat: Decimal = 0,
cholestrol: Decimal = 0,
sodium: Decimal = 0,
msnf: Decimal = 0,
other_solids: Decimal = 0,
total_solids: Decimal = 0,
water: Decimal = 0,
code: int | None = None,
id_: uuid.UUID | None = None,
is_fixture: bool | None = False,
@ -60,6 +88,21 @@ class Product:
self.is_active = is_active
self.is_purchased = is_purchased
self.is_sold = is_sold
self.protein = protein
self.carbohydrate = carbohydrate
self.total_sugar = total_sugar
self.added_sugar = added_sugar
self.total_fat = total_fat
self.saturated_fat = saturated_fat
self.trans_fat = trans_fat
self.cholestrol = cholestrol
self.sodium = sodium
self.msnf = msnf
self.other_solids = other_solids
self.total_solids = total_solids
self.water = water
if id_ is not None:
self.id = id_
if is_fixture is not None:

View File

@ -18,12 +18,24 @@ class ProductGroup:
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4)
name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False)
nutritional: Mapped[bool] = mapped_column(Boolean, nullable=False)
ice_cream: Mapped[bool] = mapped_column(Boolean, nullable=False)
is_fixture: Mapped[bool] = mapped_column(Boolean, nullable=False)
is_fixture: Mapped[bool] = mapped_column(Boolean, nullable=False)
products: Mapped[list["Product"]] = relationship("Product", back_populates="product_group")
def __init__(self, name: str, id_: uuid.UUID | None = None, is_fixture: bool | None = False) -> None:
def __init__(
self,
name: str,
nutritional: bool = False,
ice_cream: bool = False,
id_: uuid.UUID | None = None,
is_fixture: bool | None = False,
) -> None:
self.name = name
self.nutritional = nutritional
self.ice_cream = ice_cream
if id_ is not None:
self.id = id_
if is_fixture is not None:

View File

@ -40,6 +40,19 @@ def save(
is_active=data.is_active,
is_purchased=data.is_purchased,
is_sold=data.is_sold,
protein=data.protein,
carbohydrate=data.carbohydrate,
total_sugar=data.total_sugar,
added_sugar=data.added_sugar,
total_fat=data.total_fat,
saturated_fat=data.saturated_fat,
trans_fat=data.trans_fat,
cholestrol=data.cholestrol,
sodium=data.sodium,
msnf=data.msnf,
other_solids=data.other_solids,
total_solids=data.total_solids,
water=data.water,
)
item.code = db.execute(select(func.coalesce(func.max(Product.code), 0) + 1)).scalar_one()
db.add(item)
@ -89,6 +102,21 @@ def update_route(
item.is_active = data.is_active
item.is_purchased = data.is_purchased
item.is_sold = data.is_sold
item.protein = data.protein
item.carbohydrate = data.carbohydrate
item.total_sugar = data.total_sugar
item.added_sugar = data.added_sugar
item.total_fat = data.total_fat
item.saturated_fat = data.saturated_fat
item.trans_fat = data.trans_fat
item.cholestrol = data.cholestrol
item.sodium = data.sodium
item.msnf = data.msnf
item.other_solids = data.other_solids
item.total_solids = data.total_solids
item.water = data.water
if not len(data.skus):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
@ -294,6 +322,19 @@ def product_info(product: Product) -> schemas.Product:
is_purchased=product.is_purchased,
is_sold=product.is_sold,
product_group=schemas.ProductGroupLink(id_=product.product_group.id, name=product.product_group.name),
protein=product.protein,
carbohydrate=product.carbohydrate,
total_sugar=product.total_sugar,
added_sugar=product.added_sugar,
total_fat=product.total_fat,
saturated_fat=product.saturated_fat,
trans_fat=product.trans_fat,
cholestrol=product.cholestrol,
sodium=product.sodium,
msnf=product.msnf,
other_solids=product.other_solids,
total_solids=product.total_solids,
water=product.water,
)
@ -306,6 +347,19 @@ def product_blank() -> schemas.ProductBlank:
is_purchased=True,
is_sold=False,
is_fixture=False,
protein=0,
carbohydrate=0,
total_sugar=0,
added_sugar=0,
total_fat=0,
saturated_fat=0,
trans_fat=0,
cholestrol=0,
sodium=0,
msnf=0,
other_solids=0,
total_solids=0,
water=0,
)

View File

@ -22,7 +22,7 @@ def save(
) -> schemas.ProductGroup:
try:
with SessionFuture() as db:
item = ProductGroup(name=data.name)
item = ProductGroup(name=data.name, nutritional=data.nutritional, ice_cream=data.ice_cream)
db.add(item)
db.commit()
return product_group_info(item)
@ -48,6 +48,8 @@ def update_route(
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
item.nutritional = data.nutritional
item.ice_cream = data.ice_cream
db.commit()
return product_group_info(item)
except SQLAlchemyError as e:
@ -112,6 +114,8 @@ def product_group_info(item: ProductGroup) -> schemas.ProductGroup:
return schemas.ProductGroup(
id_=item.id,
name=item.name,
nutritional=item.nutritional,
ice_cream=item.ice_cream,
is_fixture=item.is_fixture,
)

View File

@ -1,5 +1,7 @@
import uuid
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field
from . import to_camel
@ -21,6 +23,22 @@ class ProductIn(BaseModel):
is_active: bool
is_purchased: bool
is_sold: bool
protein: Decimal
carbohydrate: Decimal
total_sugar: Decimal
added_sugar: Decimal
total_fat: Decimal
saturated_fat: Decimal
trans_fat: Decimal
cholestrol: Decimal
sodium: Decimal
msnf: Decimal
other_solids: Decimal
total_solids: Decimal
water: Decimal
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)

View File

@ -7,6 +7,9 @@ from . import to_camel
class ProductGroupIn(BaseModel):
name: str = Field(..., min_length=1)
nutritional: bool
ice_cream: bool
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
class ProductGroup(ProductGroupIn):

View File

@ -1,10 +1,14 @@
export class ProductGroup {
id: string | undefined;
name: string;
nutritional: boolean;
iceCream: boolean;
isFixture: boolean;
public constructor(init?: Partial<ProductGroup>) {
this.name = '';
this.nutritional = false;
this.iceCream = false;
this.isFixture = false;
Object.assign(this, init);
}

View File

@ -30,6 +30,21 @@ export class Product {
isSold: boolean;
productGroup?: ProductGroup;
protein: number;
carbohydrate: number;
totalSugar: number;
addedSugar: number;
totalFat: number;
saturatedFat: number;
transFat: number;
cholestrol: number;
sodium: number;
msnf: number;
otherSolids: number;
totalSolids: number;
water: number;
public constructor(init?: Partial<Product>) {
this.code = 0;
this.name = '';
@ -39,6 +54,22 @@ export class Product {
this.isFixture = false;
this.isPurchased = true;
this.isSold = false;
this.protein = 0;
this.carbohydrate = 0;
this.totalSugar = 0;
this.addedSugar = 0;
this.totalFat = 0;
this.saturatedFat = 0;
this.transFat = 0;
this.cholestrol = 0;
this.sodium = 0;
this.msnf = 0;
this.otherSolids = 0;
this.totalSolids = 0;
this.water = 0;
Object.assign(this, init);
}
}

View File

@ -10,6 +10,10 @@
<input matInput #nameElement formControlName="name" (keyup.enter)="save()" />
</mat-form-field>
</div>
<div class="flex flex-row justify-around content-start items-start">
<mat-checkbox formControlName="nutritional" class="flex-auto mr-5">Nutritional Information?</mat-checkbox>
<mat-checkbox formControlName="iceCream" class="flex-auto">Ice Cream?</mat-checkbox>
</div>
</form>
</mat-card-content>
<mat-card-actions>

View File

@ -15,6 +15,8 @@ export class ProductGroupDetailComponent implements OnInit, AfterViewInit {
@ViewChild('nameElement', { static: true }) nameElement?: ElementRef;
form: FormGroup<{
name: FormControl<string | null>;
nutritional: FormControl<boolean>;
iceCream: FormControl<boolean>;
}>;
item: ProductGroup = new ProductGroup();
@ -27,6 +29,8 @@ export class ProductGroupDetailComponent implements OnInit, AfterViewInit {
) {
this.form = new FormGroup({
name: new FormControl<string | null>(null),
nutritional: new FormControl<boolean>(false, { nonNullable: true }),
iceCream: new FormControl<boolean>(false, { nonNullable: true }),
});
}
@ -42,6 +46,8 @@ export class ProductGroupDetailComponent implements OnInit, AfterViewInit {
this.item = item;
this.form.setValue({
name: this.item.name,
nutritional: this.item.nutritional,
iceCream: this.item.iceCream,
});
}
@ -68,6 +74,8 @@ export class ProductGroupDetailComponent implements OnInit, AfterViewInit {
getItem(): ProductGroup {
const formModel = this.form.value;
this.item.name = formModel.name ?? '';
this.item.nutritional = formModel.nutritional ?? false;
this.item.iceCream = formModel.iceCream ?? false;
return this.item;
}
}

View File

@ -4,6 +4,7 @@ import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
@ -23,6 +24,7 @@ import { ProductGroupRoutingModule } from './product-group-routing.module';
MatPaginatorModule,
MatSortModule,
MatCardModule,
MatCheckboxModule,
MatProgressSpinnerModule,
MatInputModule,
MatButtonModule,

View File

@ -28,13 +28,74 @@
<div class="flex flex-row justify-around content-start items-start">
<mat-form-field class="flex-auto">
<mat-label>Product Type</mat-label>
<mat-select formControlName="productGroup">
<mat-select formControlName="productGroup" (selectionChange)="updatePG($event.value)">
<mat-option *ngFor="let pg of productGroups" [value]="pg.id">
{{ pg.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<h2 *ngIf="item.productGroup?.nutritional ?? false">Nutritional Information</h2>
<div
class="flex flex-row justify-around content-start items-start"
*ngIf="item.productGroup?.nutritional ?? false"
>
<mat-form-field class="flex-auto mr-5">
<mat-label>Protein</mat-label>
<input matInput formControlName="protein" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Carbohydrate</mat-label>
<input matInput formControlName="carbohydrate" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Total Sugar</mat-label>
<input matInput formControlName="totalSugar" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Added Sugar</mat-label>
<input matInput formControlName="addedSugar" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Total Fat</mat-label>
<input matInput formControlName="totalFat" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Saturated Fat</mat-label>
<input matInput formControlName="saturatedFat" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Trans Fat</mat-label>
<input matInput formControlName="transFat" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Cholestrol</mat-label>
<input matInput formControlName="cholestrol" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Sodium</mat-label>
<input matInput formControlName="sodium" />
</mat-form-field>
</div>
<h2 *ngIf="item.productGroup?.iceCream ?? false">Ice Cream Information</h2>
<div class="flex flex-row justify-around content-start items-start" *ngIf="item.productGroup?.iceCream ?? false">
<mat-form-field class="flex-auto mr-5">
<mat-label>MSNF</mat-label>
<input matInput formControlName="msnf" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Other Solids</mat-label>
<input matInput formControlName="otherSolids" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Total Solids</mat-label>
<input matInput formControlName="totalSolids" />
</mat-form-field>
<mat-form-field class="flex-auto mr-5">
<mat-label>Water</mat-label>
<input matInput formControlName="water" />
</mat-form-field>
</div>
<h2>Stock Keeping Units</h2>
<div
formGroupName="addRow"

View File

@ -35,6 +35,21 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
isSold: FormControl<boolean>;
isActive: FormControl<boolean>;
productGroup: FormControl<string | null>;
protein: FormControl<number>;
carbohydrate: FormControl<number>;
totalSugar: FormControl<number>;
addedSugar: FormControl<number>;
totalFat: FormControl<number>;
saturatedFat: FormControl<number>;
transFat: FormControl<number>;
cholestrol: FormControl<number>;
sodium: FormControl<number>;
msnf: FormControl<number>;
otherSolids: FormControl<number>;
totalSolids: FormControl<number>;
water: FormControl<number>;
}>;
productGroups: ProductGroup[] = [];
@ -66,6 +81,21 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
isSold: new FormControl<boolean>(true, { nonNullable: true }),
isActive: new FormControl<boolean>(true, { nonNullable: true }),
productGroup: new FormControl<string | null>(null),
protein: new FormControl<number>(0, { nonNullable: true }),
carbohydrate: new FormControl<number>(0, { nonNullable: true }),
totalSugar: new FormControl<number>(0, { nonNullable: true }),
addedSugar: new FormControl<number>(0, { nonNullable: true }),
totalFat: new FormControl<number>(0, { nonNullable: true }),
saturatedFat: new FormControl<number>(0, { nonNullable: true }),
transFat: new FormControl<number>(0, { nonNullable: true }),
cholestrol: new FormControl<number>(0, { nonNullable: true }),
sodium: new FormControl<number>(0, { nonNullable: true }),
msnf: new FormControl<number>(0, { nonNullable: true }),
otherSolids: new FormControl<number>(0, { nonNullable: true }),
totalSolids: new FormControl<number>(0, { nonNullable: true }),
water: new FormControl<number>(0, { nonNullable: true }),
});
}
@ -81,6 +111,7 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
}
showItem(item: Product) {
item.productGroup = this.productGroups.find((x) => x.id === item.productGroup?.id);
this.item = item;
this.form.setValue({
code: this.item.code || '(Auto)',
@ -97,6 +128,21 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
isSold: this.item.isSold,
isActive: this.item.isActive,
productGroup: this.item.productGroup ? this.item.productGroup.id ?? '' : '',
protein: this.item.protein,
carbohydrate: this.item.carbohydrate,
totalSugar: this.item.totalSugar,
addedSugar: this.item.addedSugar,
totalFat: this.item.totalFat,
saturatedFat: this.item.saturatedFat,
transFat: this.item.transFat,
cholestrol: this.item.cholestrol,
sodium: this.item.sodium,
msnf: this.item.msnf,
otherSolids: this.item.otherSolids,
totalSolids: this.item.totalSolids,
water: this.item.water,
});
}
@ -108,6 +154,10 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
}, 0);
}
updatePG(id: string) {
this.item.productGroup = this.productGroups.find((x) => x.id == id);
}
addRow() {
const formValue = this.form.value.addRow;
if (formValue === undefined) {
@ -220,10 +270,22 @@ export class ProductDetailComponent implements OnInit, AfterViewInit {
this.item.isPurchased = formModel.isPurchased ?? true;
this.item.isSold = formModel.isSold ?? false;
this.item.isActive = formModel.isActive ?? true;
if (this.item.productGroup === null || this.item.productGroup === undefined) {
this.item.productGroup = new ProductGroup();
}
this.item.productGroup.id = formModel.productGroup ?? '';
this.item.protein = formModel.protein ?? 0;
this.item.carbohydrate = formModel.carbohydrate ?? 0;
this.item.totalSugar = formModel.totalSugar ?? 0;
this.item.addedSugar = formModel.addedSugar ?? 0;
this.item.totalFat = formModel.totalFat ?? 0;
this.item.saturatedFat = formModel.saturatedFat ?? 0;
this.item.transFat = formModel.transFat ?? 0;
this.item.cholestrol = formModel.cholestrol ?? 0;
this.item.sodium = formModel.sodium ?? 0;
this.item.msnf = formModel.msnf ?? 0;
this.item.otherSolids = formModel.otherSolids ?? 0;
this.item.totalSolids = formModel.totalSolids ?? 0;
this.item.water = formModel.water ?? 0;
return this.item;
}
}