Fix: Recipe now checks for recursion (hopefully)

Feature: Recipe prices are now calculated based on periods and saved
Feature: The recipe export excel now has prices
This commit is contained in:
Amritanshu Agrawal 2023-08-12 08:29:21 +05:30
parent 72843feaac
commit 1705d58dbc
10 changed files with 321 additions and 73 deletions

View File

@ -5,41 +5,46 @@ Revises: a1372ed99c45
Create Date: 2023-04-14 07:50:22.110724 Create Date: 2023-04-14 07:50:22.110724
""" """
from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '12262aadbc08' revision = "12262aadbc08"
down_revision = 'a1372ed99c45' down_revision = "a1372ed99c45"
branch_labels = None branch_labels = None
depends_on = None depends_on = None
def upgrade(): def upgrade():
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.create_table('recipe_templates', op.create_table(
sa.Column('id', sa.Uuid(), nullable=False), "recipe_templates",
sa.Column('name', sa.Unicode(), nullable=False), sa.Column("id", sa.Uuid(), nullable=False),
sa.Column('date', sa.Date(), nullable=False), sa.Column("name", sa.Unicode(), nullable=False),
sa.Column('text', sa.Unicode(), nullable=False), sa.Column("date", sa.Date(), nullable=False),
sa.Column('selected', sa.Boolean(), nullable=False), sa.Column("text", sa.Unicode(), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_recipe_templates')), sa.Column("selected", sa.Boolean(), nullable=False),
sa.UniqueConstraint('name', name=op.f('uq_recipe_templates_name')) sa.PrimaryKeyConstraint("id", name=op.f("pk_recipe_templates")),
sa.UniqueConstraint("name", name=op.f("uq_recipe_templates_name")),
) )
op.create_index('only_one_selected_template', 'recipe_templates', ['selected'], unique=True, postgresql_where=sa.text('selected = true')) op.create_index(
op.alter_column('recipes', 'notes', "only_one_selected_template",
existing_type=sa.VARCHAR(length=255), "recipe_templates",
type_=sa.Text(), ["selected"],
existing_nullable=False) unique=True,
postgresql_where=sa.text("selected = true"),
)
op.alter_column("recipes", "notes", existing_type=sa.VARCHAR(length=255), type_=sa.Text(), existing_nullable=False)
# ### end Alembic commands ### # ### end Alembic commands ###
def downgrade(): def downgrade():
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.alter_column('recipes', 'notes', op.alter_column("recipes", "notes", existing_type=sa.Text(), type_=sa.VARCHAR(length=255), existing_nullable=False)
existing_type=sa.Text(), op.drop_index(
type_=sa.VARCHAR(length=255), "only_one_selected_template", table_name="recipe_templates", postgresql_where=sa.text("selected = true")
existing_nullable=False) )
op.drop_index('only_one_selected_template', table_name='recipe_templates', postgresql_where=sa.text('selected = true')) op.drop_table("recipe_templates")
op.drop_table('recipe_templates')
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@ -1,4 +1,4 @@
"""FP """Fingerprint Index
Revision ID: 48af31eb6f3f Revision ID: 48af31eb6f3f
Revises: 12262aadbc08 Revises: 12262aadbc08
@ -6,19 +6,18 @@ Create Date: 2023-08-07 13:01:05.401492
""" """
from alembic import op from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '48af31eb6f3f' revision = "48af31eb6f3f"
down_revision = '12262aadbc08' down_revision = "12262aadbc08"
branch_labels = None branch_labels = None
depends_on = None depends_on = None
def upgrade(): def upgrade():
op.create_unique_constraint(op.f('uq_fingerprints_date'), 'fingerprints', ['date', 'employee_id']) op.create_unique_constraint(op.f("uq_fingerprints_date"), "fingerprints", ["date", "employee_id"])
def downgrade(): def downgrade():
op.drop_constraint(op.f('uq_fingerprints_date'), 'fingerprints', type_='unique') op.drop_constraint(op.f("uq_fingerprints_date"), "fingerprints", type_="unique")

View File

@ -8,8 +8,9 @@ Create Date: 2023-03-31 05:03:40.408240
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.dialects import postgresql
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "a1372ed99c45" revision = "a1372ed99c45"
@ -62,15 +63,15 @@ def upgrade():
op.add_column("recipes", sa.Column("garnishing", sa.Text(), nullable=False, server_default="")) op.add_column("recipes", sa.Column("garnishing", sa.Text(), nullable=False, server_default=""))
op.add_column("recipes", sa.Column("plating", sa.Text(), nullable=False, server_default="")) op.add_column("recipes", sa.Column("plating", sa.Text(), nullable=False, server_default=""))
op.drop_constraint(op.f("uq_recipes_sku_id"), "recipes", type_="unique") op.drop_constraint(op.f("uq_recipes_sku_id"), "recipes", type_="unique")
op.alter_column("recipes", "notes", existing_type=sa.VARCHAR(length=255),type=sa.Text(), nullable=False) op.alter_column("recipes", "notes", existing_type=sa.VARCHAR(length=255), type=sa.Text(), nullable=False)
op.create_unique_constraint(op.f('uq_recipes_sku_id'), 'recipes', ['sku_id', 'date']) op.create_unique_constraint(op.f("uq_recipes_sku_id"), "recipes", ["sku_id", "date"])
op.create_index(op.f("ix_recipes_date"), "recipes", ["date"], unique=False) op.create_index(op.f("ix_recipes_date"), "recipes", ["date"], unique=False)
op.drop_constraint("fk_recipes_period_id_periods", "recipes", type_="foreignkey") op.drop_constraint("fk_recipes_period_id_periods", "recipes", type_="foreignkey")
op.drop_column("recipes", "period_id") op.drop_column("recipes", "period_id")
op.drop_column('recipes', 'sale_price') op.drop_column("recipes", "sale_price")
op.drop_column('recipes', 'cost_price') op.drop_column("recipes", "cost_price")
op.add_column('recipe_items', sa.Column('description', sa.Text(), nullable=False, server_default="")) op.add_column("recipe_items", sa.Column("description", sa.Text(), nullable=False, server_default=""))
op.drop_column('recipe_items', 'price') op.drop_column("recipe_items", "price")
op.alter_column("role_permissions", "permission_id", existing_type=sa.UUID(), nullable=False) op.alter_column("role_permissions", "permission_id", existing_type=sa.UUID(), nullable=False)
op.alter_column("role_permissions", "role_id", existing_type=sa.UUID(), nullable=False) op.alter_column("role_permissions", "role_id", existing_type=sa.UUID(), nullable=False)
op.create_unique_constraint( op.create_unique_constraint(

View File

@ -0,0 +1,39 @@
"""price
Revision ID: ba0fff092981
Revises: 48af31eb6f3f
Create Date: 2023-08-11 18:12:51.293741
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "ba0fff092981"
down_revision = "48af31eb6f3f"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"prices",
sa.Column("id", sa.Uuid(), nullable=False),
sa.Column("period_id", sa.Uuid(), nullable=False),
sa.Column("product_id", sa.Uuid(), nullable=False),
sa.Column("price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.ForeignKeyConstraint(["period_id"], ["periods.id"], name=op.f("fk_prices_period_id_periods")),
sa.ForeignKeyConstraint(["product_id"], ["products.id"], name=op.f("fk_prices_product_id_products")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_prices")),
sa.UniqueConstraint("period_id", "product_id", name=op.f("uq_prices_period_id")),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("prices")
# ### end Alembic commands ###

View File

@ -19,6 +19,7 @@ from ..models.journal import Journal # noqa: F401
from ..models.login_history import LoginHistory # noqa: F401 from ..models.login_history import LoginHistory # noqa: F401
from ..models.period import Period # noqa: F401 from ..models.period import Period # noqa: F401
from ..models.permission import Permission # noqa: F401 from ..models.permission import Permission # noqa: F401
from ..models.price import Price # noqa: F401
from ..models.product import Product # noqa: F401 from ..models.product import Product # noqa: F401
from ..models.product_group import ProductGroup # noqa: F401 from ..models.product_group import ProductGroup # noqa: F401
from ..models.rate_contract import RateContract # noqa: F401 from ..models.rate_contract import RateContract # noqa: F401

View File

@ -0,0 +1,51 @@
from __future__ import annotations
import uuid
from decimal import Decimal
from typing import TYPE_CHECKING
from sqlalchemy import ForeignKey, Numeric, UniqueConstraint, Uuid
from sqlalchemy.orm import Mapped, mapped_column, relationship
from ..db.base_class import reg
if TYPE_CHECKING:
from .period import Period
from .product import Product
@reg.mapped_as_dataclass(unsafe_hash=True)
class Price:
__tablename__ = "prices"
__table_args__ = (UniqueConstraint("period_id", "product_id"),)
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4)
period_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("periods.id"), nullable=False)
product_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("products.id"), nullable=False)
price: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False)
period: Mapped["Period"] = relationship("Period")
product: Mapped["Product"] = relationship("Product")
def __init__(
self,
price: Decimal,
period_id: uuid.UUID | None = None,
product_id: uuid.UUID | None = None,
period: "Period" | None = None,
product: "Product" | None = None,
id_: uuid.UUID | None = None,
):
self.price = price
if period_id is not None:
self.period_id = period_id
if product_id is not None:
self.product_id = product_id
if period is not None and (period.id is not None or period_id is None):
self.period = period
if product is not None and (product.id is not None or product_id is None):
self.product = product
if id_ is not None:
self.id = id_

View File

@ -0,0 +1,131 @@
import uuid
from decimal import Decimal
from brewman.models.batch import Batch
from brewman.models.cost_centre import CostCentre
from brewman.models.inventory import Inventory
from brewman.models.journal import Journal
from brewman.models.period import Period
from brewman.models.price import Price
from brewman.models.voucher import Voucher
from brewman.models.voucher_type import VoucherType
from fastapi import HTTPException, status
from sqlalchemy import distinct, func, select
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..models.recipe import Recipe
from ..models.recipe_item import RecipeItem
from ..models.stock_keeping_unit import StockKeepingUnit
def calculate_prices(period_id: uuid.UUID, db: Session):
try:
item: Period = db.execute(select(Period).where(Period.id == period_id)).scalar_one()
recipes = set(
db.execute(select(distinct(StockKeepingUnit.product_id)).join(StockKeepingUnit.recipes)).scalars().all()
)
ingredients = set(db.execute(select(distinct(RecipeItem.product_id))).scalars().all())
issued_products = get_issue_prices(item, ingredients - recipes, db)
left = ingredients - recipes - issued_products.keys()
purchased_products = get_issue_prices(item, left, db)
left -= purchased_products.keys()
rest = get_rest(left, db)
prices = issued_products | purchased_products | rest
while len(recipes) > 0:
calculate_recipes(recipes, prices, db)
for pid, price in prices.items():
db.execute(
pg_insert(Price)
.values(product_id=pid, price=price, period_id=item.id)
.on_conflict_do_update(constraint="uq_prices_period_id", set_=dict(price=price))
)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
def get_issue_prices(period: Period, products: set[uuid.UUID], db: Session) -> dict[uuid.UUID, Decimal]:
sum_quantity = func.sum(
Inventory.quantity * StockKeepingUnit.fraction * StockKeepingUnit.product_yield * Journal.debit
).label("quantity")
sum_net = func.sum(Inventory.rate * Inventory.quantity * Journal.debit).label("net")
d: dict[uuid.UUID, Decimal] = {}
query: list[tuple[uuid.UUID, Decimal]] = db.execute(
select(StockKeepingUnit.product_id, sum_net / sum_quantity)
.join(Inventory.batch)
.join(Batch.sku)
.join(Inventory.voucher)
.join(Voucher.journals)
.where(
Voucher.date_ >= period.valid_from,
Voucher.date_ <= period.valid_till,
Voucher.voucher_type.in_([VoucherType.ISSUE, VoucherType.CLOSING_STOCK]),
Journal.cost_centre_id != CostCentre.cost_centre_purchase(),
StockKeepingUnit.product_id.in_(products),
)
.group_by(StockKeepingUnit.product_id, Journal.debit)
).all()
for id, amount in query:
d[id] = amount
return d
def get_purchase_prices(period: Period, req: set[uuid.UUID], db: Session) -> dict[uuid.UUID, Decimal]:
sum_quantity = func.sum(
Inventory.quantity * StockKeepingUnit.fraction * StockKeepingUnit.product_yield * Journal.debit
).label("quantity")
sum_net = func.sum(Inventory.rate * Inventory.quantity * Journal.debit).label("net")
d: dict[uuid.UUID, Decimal] = {}
query: list[tuple[uuid.UUID, Decimal]] = db.execute(
select(StockKeepingUnit.product_id, sum_net / sum_quantity)
.join(Inventory.batch)
.join(Batch.sku)
.join(Inventory.voucher)
.join(Voucher.journals)
.where(
Voucher.date_ >= period.valid_from,
Voucher.date_ <= period.valid_till,
Voucher.voucher_type == VoucherType.PURCHASE,
StockKeepingUnit.product_id.in_(req),
)
.group_by(StockKeepingUnit.product_id, Journal.debit)
).all()
for id, amount in query:
d[id] = amount
return d
def get_rest(req: set[uuid.UUID], db: Session) -> dict[uuid.UUID, Decimal]:
d: dict[uuid.UUID, Decimal] = {}
query = db.execute(
select(
StockKeepingUnit.product_id,
StockKeepingUnit.cost_price / (StockKeepingUnit.fraction * StockKeepingUnit.product_yield),
).where(StockKeepingUnit.product_id.in_(req))
).all()
for id, amount in query:
d[id] = amount
return d
def calculate_recipes(recipes: set[uuid.UUID], prices: dict[uuid.UUID, Decimal], db: Session) -> None:
sq = select(RecipeItem.recipe_id).where(RecipeItem.product_id.in_(recipes))
items = (
db.execute(
select(Recipe).join(Recipe.sku).where(StockKeepingUnit.product_id.in_(recipes), Recipe.id.notin_(sq))
)
.scalars()
.all()
)
for item in items:
cost = sum(i.quantity * prices[i.product_id] for i in item.items) / (item.recipe_yield * item.sku.fraction)
prices[item.sku.product_id] = cost
recipes.remove(item.sku.product_id)

View File

@ -11,6 +11,8 @@ from typing import Sequence
import brewman.schemas.recipe as schemas import brewman.schemas.recipe as schemas
import brewman.schemas.recipe_item as rischemas import brewman.schemas.recipe_item as rischemas
from brewman.models.price import Price
from brewman.routers.calculate_prices import calculate_prices
from fastapi import APIRouter, Depends, HTTPException, Request, Security, status from fastapi import APIRouter, Depends, HTTPException, Request, Security, status
from fastapi.responses import FileResponse, StreamingResponse from fastapi.responses import FileResponse, StreamingResponse
from openpyxl import Workbook from openpyxl import Workbook
@ -72,8 +74,8 @@ def save(
r_item.recipe_id = recipe.id r_item.recipe_id = recipe.id
db.add(r_item) db.add(r_item)
# TODO: Check recursion db.flush()
# check_recursion(set([recipe_sku.product_id]), set(), recipe, db) check_recursion(recipe_sku.product_id, set(), db)
db.commit() db.commit()
return recipe_info(recipe) return recipe_info(recipe)
except SQLAlchemyError as e: except SQLAlchemyError as e:
@ -130,7 +132,8 @@ async def update_route(
RecipeItem(product_id=product.id, quantity=quantity, description=d_item.description) RecipeItem(product_id=product.id, quantity=quantity, description=d_item.description)
) )
check_recursion(set([sku.product_id]), set(), db) db.flush()
check_recursion(sku.product_id, set(), db)
db.commit() db.commit()
return recipe_info(recipe) return recipe_info(recipe)
except SQLAlchemyError as e: except SQLAlchemyError as e:
@ -140,26 +143,23 @@ async def update_route(
) )
def check_recursion(products: set[uuid.UUID], visited: set[uuid.UUID], db: Session) -> None: def check_recursion(product: uuid.UUID, visited: set[uuid.UUID], db: Session) -> None:
sq = ( if product in visited:
select(func.distinct(RecipeItem.product_id))
.join(Recipe.items)
.join(Recipe.sku)
.where(StockKeepingUnit.product_id.in_(products))
)
ingredient_product_ids = (
db.execute(select(StockKeepingUnit.product_id).join(Recipe.sku).where(StockKeepingUnit.product_id.in_(sq)))
.scalars()
.all()
)
if len(ingredient_product_ids) == 0:
return
if (visited | products) & set(ingredient_product_ids):
raise HTTPException( raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Recipe recursion. Some ingredient recipe contains parent recipe.", detail="Recipe recursion. Some ingredient recipe contains parent recipe.",
) )
check_recursion(set(ingredient_product_ids), visited | products, db) recipe: Recipe = (
db.execute(select(Recipe).join(Recipe.items).join(Recipe.sku).where(StockKeepingUnit.product_id == product))
.unique()
.scalar_one_or_none()
)
if recipe is None:
return
visited.add(product)
for i in recipe.items:
check_recursion(i.product_id, visited, db)
visited.remove(product)
@router.delete("/{id_}", response_model=None) @router.delete("/{id_}", response_model=None)
@ -169,7 +169,6 @@ def delete_route(
user: UserToken = Security(get_user, scopes=["recipes"]), user: UserToken = Security(get_user, scopes=["recipes"]),
) -> None: ) -> None:
with SessionFuture() as db: with SessionFuture() as db:
recipe: Recipe = db.execute(select(Recipe).where(Recipe.id == id_)).scalar_one()
recipe_ids: Sequence[uuid.UUID] = ( recipe_ids: Sequence[uuid.UUID] = (
db.execute( db.execute(
select(func.distinct(RecipeItem.recipe_id)).where( select(func.distinct(RecipeItem.recipe_id)).where(
@ -268,7 +267,22 @@ def show_pdf(
@router.get("/xlsx", response_class=StreamingResponse) @router.get("/xlsx", response_class=StreamingResponse)
def get_report( def get_report(
p: uuid.UUID | None = None, p: uuid.UUID | None = None,
t: uuid.UUID | None = None,
) -> StreamingResponse: ) -> StreamingResponse:
with SessionFuture() as db:
calculate_prices(t, db)
db.commit()
prices: list[tuple[str, str, Decimal]] = []
with SessionFuture() as db:
pq = (
db.execute(select(Price).where(Price.period_id == t).options(joinedload(Price.product, innerjoin=True)))
.unique()
.scalars()
.all()
)
prices = [(i.product.name, i.product.fraction_units, i.price) for i in pq]
list_: Sequence[Recipe] = []
with SessionFuture() as db: with SessionFuture() as db:
q = ( q = (
select(Recipe) select(Recipe)
@ -282,17 +296,25 @@ def get_report(
) )
if p is not None: if p is not None:
q = q.where(Recipe.sku, StockKeepingUnit.product, Product.product_group_id == p) q = q.where(Recipe.sku, StockKeepingUnit.product, Product.product_group_id == p)
list_: Sequence[Recipe] = db.execute(q).unique().scalars().all() list_ = db.execute(q).unique().scalars().all()
e = excel(sorted(list_, key=lambda r: r.sku.product.name)) e = excel(prices, sorted(list_, key=lambda r: r.sku.product.name))
e.seek(0) e.seek(0)
headers = {"Content-Disposition": "attachment; filename = recipe.xlsx"} headers = {"Content-Disposition": "attachment; filename = recipe.xlsx"}
return StreamingResponse(e, media_type="text/xlsx", headers=headers) return StreamingResponse(e, media_type="text/xlsx", headers=headers)
def excel(recipes: list[Recipe]) -> BytesIO: def excel(prices: list[tuple[str, str, Decimal, Decimal, Decimal]], recipes: list[Recipe]) -> BytesIO:
wb = Workbook() wb = Workbook()
wb.active.title = "Rate List" wb.active.title = "Rate List"
wb.active.cell(row=1, column=1, value="Name")
wb.active.cell(row=1, column=2, value="Units")
wb.active.cell(row=1, column=3, value="Rate")
for i, p in enumerate(prices, start=2):
wb.active.cell(row=i, column=1, value=p[0])
wb.active.cell(row=i, column=2, value=p[1])
wb.active.cell(row=i, column=3, value=p[2])
pgs = set([x.sku.product.product_group.name for x in recipes]) pgs = set([x.sku.product.product_group.name for x in recipes])
for pg in pgs: for pg in pgs:
wb.create_sheet(pg) wb.create_sheet(pg)
@ -301,6 +323,7 @@ def excel(recipes: list[Recipe]) -> BytesIO:
for recipe in recipes: for recipe in recipes:
ws = wb[recipe.sku.product.product_group.name] ws = wb[recipe.sku.product.product_group.name]
row = rows[recipe.sku.product.product_group.name] row = rows[recipe.sku.product.product_group.name]
print(row)
ings = len(recipe.items) ings = len(recipe.items)
ing_from = row + 2 ing_from = row + 2
ing_till = ing_from + ings - 1 ing_till = ing_from + ings - 1
@ -321,11 +344,11 @@ def excel(recipes: list[Recipe]) -> BytesIO:
ws.cell(row=row, column=1, value=item.product.name).style = "ing" ws.cell(row=row, column=1, value=item.product.name).style = "ing"
ws.cell(row=row, column=2, value=item.product.fraction_units).style = "unit" ws.cell(row=row, column=2, value=item.product.fraction_units).style = "unit"
ws.cell(row=row, column=3, value=item.quantity).style = "ing" ws.cell(row=row, column=3, value=item.quantity).style = "ing"
ws.cell(row=row, column=4, value="=VLOOKUP(A:A,'Rate List'!A:G,7,0)").style = "ing" ws.cell(row=row, column=4, value="=VLOOKUP(A:A,'Rate List'!A:C,3,0)").style = "ing"
ws.cell(row=row, column=5, value=f"=C{row}*D{row}").style = "ing" ws.cell(row=row, column=5, value=f"=C{row}*D{row}").style = "ing"
rows[recipe.sku.product.product_group.name] = row + 1 rows[recipe.sku.product.product_group.name] = row + 1
virtual_workbook = BytesIO() virtual_workbook = BytesIO()
wb.save(virtual_workbook) wb.save(virtual_workbook)
return virtual_workbook return virtual_workbook

View File

@ -2,7 +2,7 @@
<mat-card-header> <mat-card-header>
<mat-card-title-group> <mat-card-title-group>
<mat-card-title>Recipes</mat-card-title> <mat-card-title>Recipes</mat-card-title>
<a mat-icon-button href="{{ excelLink() }}"> <a mat-icon-button [href]="'/api/recipes/xlsx?t=' + period.id">
<mat-icon>save_alt</mat-icon> <mat-icon>save_alt</mat-icon>
</a> </a>
<a mat-button [routerLink]="['/recipes', 'new']"> <a mat-button [routerLink]="['/recipes', 'new']">

View File

@ -31,6 +31,7 @@ export class RecipeListComponent implements OnInit {
list: Recipe[] = []; list: Recipe[] = [];
data: BehaviorSubject<Recipe[]> = new BehaviorSubject<Recipe[]>([]); data: BehaviorSubject<Recipe[]> = new BehaviorSubject<Recipe[]>([]);
dataSource: RecipeListDatasource = new RecipeListDatasource(this.productGroupFilter, this.data); dataSource: RecipeListDatasource = new RecipeListDatasource(this.productGroupFilter, this.data);
period: Period = new Period();
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns = ['name', 'yield', 'date', 'source']; displayedColumns = ['name', 'yield', 'date', 'source'];
@ -44,13 +45,14 @@ export class RecipeListComponent implements OnInit {
productGroup: new FormControl<ProductGroup | string | null>(null), productGroup: new FormControl<ProductGroup | string | null>(null),
}); });
// Listen to Payment Account Change // Listen to Payment Account Change
this.form.controls.period.valueChanges.subscribe((x) => this.form.controls.period.valueChanges.subscribe((x) => {
this.router.navigate([], { this.router.navigate([], {
relativeTo: this.route, relativeTo: this.route,
queryParams: { p: x.id }, queryParams: { p: x.id },
replaceUrl: true, replaceUrl: true,
}), });
); this.period = x;
});
} }
ngOnInit() { ngOnInit() {
@ -73,8 +75,4 @@ export class RecipeListComponent implements OnInit {
filterProductGroup(val: string) { filterProductGroup(val: string) {
this.productGroupFilter.next(val || ''); this.productGroupFilter.next(val || '');
} }
excelLink() {
return `/api/recipes/xlsx`;
}
} }