Feature: Added a Mozimo Daily Register which shows products from Menu Items category
This commit is contained in:
@ -53,6 +53,7 @@ from .routers.reports import (
|
|||||||
daybook,
|
daybook,
|
||||||
entries,
|
entries,
|
||||||
ledger,
|
ledger,
|
||||||
|
mozimo_daily_register,
|
||||||
mozimo_product_register,
|
mozimo_product_register,
|
||||||
net_transactions,
|
net_transactions,
|
||||||
non_contract_purchase,
|
non_contract_purchase,
|
||||||
@ -115,6 +116,7 @@ app.include_router(trial_balance.router, prefix="/api/trial-balance", tags=["rep
|
|||||||
app.include_router(entries.router, prefix="/api/entries", tags=["reports"])
|
app.include_router(entries.router, prefix="/api/entries", tags=["reports"])
|
||||||
app.include_router(batch_integrity.router, prefix="/api/batch-integrity", tags=["reports"])
|
app.include_router(batch_integrity.router, prefix="/api/batch-integrity", tags=["reports"])
|
||||||
app.include_router(non_contract_purchase.router, prefix="/api/non-contract-purchase", tags=["reports"])
|
app.include_router(non_contract_purchase.router, prefix="/api/non-contract-purchase", tags=["reports"])
|
||||||
|
app.include_router(mozimo_daily_register.router, prefix="/api/mozimo-daily-register", tags=["mozimo"])
|
||||||
app.include_router(mozimo_product_register.router, prefix="/api/mozimo-product-register", tags=["mozimo"])
|
app.include_router(mozimo_product_register.router, prefix="/api/mozimo-product-register", tags=["mozimo"])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
219
brewman/brewman/routers/reports/mozimo_daily_register.py
Normal file
219
brewman/brewman/routers/reports/mozimo_daily_register.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from datetime import UTC, date, datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException, Request, Security, status
|
||||||
|
from sqlalchemy import desc, func, or_
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy.sql.expression import select
|
||||||
|
|
||||||
|
import brewman.schemas.mozimo_daily_register as schemas
|
||||||
|
|
||||||
|
from ...core.security import get_current_active_user as get_user
|
||||||
|
from ...core.session import get_date, set_date
|
||||||
|
from ...db.session import SessionFuture
|
||||||
|
from ...models.mozimo_stock_register import MozimoStockRegister
|
||||||
|
from ...models.product import Product
|
||||||
|
from ...models.stock_keeping_unit import StockKeepingUnit
|
||||||
|
from ...schemas.user import UserToken
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=schemas.MozimoDailyRegister)
|
||||||
|
def show_blank(
|
||||||
|
request: Request,
|
||||||
|
user: UserToken = Security(get_user, scopes=["product-ledger"]),
|
||||||
|
) -> schemas.MozimoDailyRegister:
|
||||||
|
return schemas.MozimoDailyRegister(date_=get_date(request.session), body=[])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{date_}", response_model=schemas.MozimoDailyRegister)
|
||||||
|
def show_data(
|
||||||
|
date_: str,
|
||||||
|
request: Request,
|
||||||
|
user: UserToken = Security(get_user, scopes=["product-ledger"]),
|
||||||
|
) -> schemas.MozimoDailyRegister:
|
||||||
|
with SessionFuture() as db:
|
||||||
|
d = datetime.strptime(date_, "%d-%b-%Y").date()
|
||||||
|
body = build_report(d, db)
|
||||||
|
set_date(date_, request.session)
|
||||||
|
return schemas.MozimoDailyRegister(
|
||||||
|
date=d,
|
||||||
|
body=body,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_report(date_: date, db: Session) -> list[schemas.MozimoDailyRegisterItem]:
|
||||||
|
body = []
|
||||||
|
products = (
|
||||||
|
db.execute(
|
||||||
|
select(StockKeepingUnit)
|
||||||
|
.join(StockKeepingUnit.product)
|
||||||
|
.where(Product.product_group_id == uuid.UUID("dad46805-f577-4e5b-8073-9b788e0173fc")) # Menu items
|
||||||
|
.order_by(Product.name, StockKeepingUnit.units)
|
||||||
|
)
|
||||||
|
.scalars()
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
for sku in products:
|
||||||
|
ob = opening_balance(sku.id, date_, db)
|
||||||
|
item = (
|
||||||
|
db.execute(
|
||||||
|
select(MozimoStockRegister).where(
|
||||||
|
MozimoStockRegister.sku_id == sku.id,
|
||||||
|
MozimoStockRegister.date_ == date_,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.scalars()
|
||||||
|
.one_or_none()
|
||||||
|
)
|
||||||
|
body.append(
|
||||||
|
schemas.MozimoDailyRegisterItem(
|
||||||
|
id_=None if item is None else item.id_,
|
||||||
|
product=schemas.ProductLink(id_=sku.id, name=f"{sku.product.name} ({sku.units})"),
|
||||||
|
opening=ob,
|
||||||
|
received=Decimal(0) if item is None else item.received,
|
||||||
|
sale=Decimal(0) if item is None else item.sale,
|
||||||
|
nc=Decimal(0) if item is None else item.nc,
|
||||||
|
display=None if item is None else item.display,
|
||||||
|
ageing=None if item is None else item.ageing,
|
||||||
|
last_edit_date=None if item is None else item.last_edit_date,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
def opening_balance(product_id: uuid.UUID, start_date: date, db: Session) -> Decimal:
|
||||||
|
opening_physical = (
|
||||||
|
db.execute(
|
||||||
|
select(MozimoStockRegister)
|
||||||
|
.order_by(desc(MozimoStockRegister.date_))
|
||||||
|
.where(
|
||||||
|
MozimoStockRegister.sku_id == product_id,
|
||||||
|
MozimoStockRegister.date_ < start_date,
|
||||||
|
or_(
|
||||||
|
MozimoStockRegister.display != None, # noqa: E711
|
||||||
|
MozimoStockRegister.ageing != None, # noqa: E711
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.order_by(desc(MozimoStockRegister.date_))
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
.scalars()
|
||||||
|
.one_or_none()
|
||||||
|
)
|
||||||
|
physical = ((opening_physical.display or 0) + (opening_physical.ageing or 0)) if opening_physical is not None else 0
|
||||||
|
query_ = select(func.sum(MozimoStockRegister.received - MozimoStockRegister.sale - MozimoStockRegister.nc)).where(
|
||||||
|
MozimoStockRegister.sku_id == product_id,
|
||||||
|
MozimoStockRegister.date_ < start_date,
|
||||||
|
)
|
||||||
|
if opening_physical is not None:
|
||||||
|
query_ = query_.where(MozimoStockRegister.date_ > opening_physical.date_)
|
||||||
|
calculated = db.execute(query_).scalar() or 0
|
||||||
|
return physical + calculated
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{date_}", response_model=schemas.MozimoDailyRegister)
|
||||||
|
def save_route(
|
||||||
|
date_: str,
|
||||||
|
request: Request,
|
||||||
|
data: schemas.MozimoDailyRegister,
|
||||||
|
user: UserToken = Security(get_user, scopes=["product-ledger"]),
|
||||||
|
) -> schemas.MozimoDailyRegister:
|
||||||
|
d = datetime.strptime(date_, "%d-%b-%Y").date()
|
||||||
|
try:
|
||||||
|
if any(i for i in data.body if i.received < 0):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Received cannot be less than 0.",
|
||||||
|
)
|
||||||
|
if any(i for i in data.body if i.sale < 0):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Sale cannot be less than 0.",
|
||||||
|
)
|
||||||
|
if any(i for i in data.body if i.nc < 0):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="No Charge cannot be less than 0.",
|
||||||
|
)
|
||||||
|
if any(i for i in data.body if i.display is not None and i.display < 0):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Quantity on display can be Blank, but cannot be less than 0.",
|
||||||
|
)
|
||||||
|
if any(i for i in data.body if i.ageing is not None and i.ageing < 0):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Quantity in ageing room can be Blank, but cannot be less than 0.",
|
||||||
|
)
|
||||||
|
with SessionFuture() as db:
|
||||||
|
now = datetime.now(UTC).replace(tzinfo=None)
|
||||||
|
for item in data.body:
|
||||||
|
ob = opening_balance(item.product.id_, d, db)
|
||||||
|
if item.ageing is not None or item.display is not None:
|
||||||
|
closing = (item.ageing or 0) + (item.display or 0)
|
||||||
|
else:
|
||||||
|
closing = ob + item.received - item.sale - item.nc
|
||||||
|
if closing < 0:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Closing Stock cannot be less than 0.",
|
||||||
|
)
|
||||||
|
|
||||||
|
for item in data.body:
|
||||||
|
is_blank = (
|
||||||
|
item.received == 0
|
||||||
|
and item.sale == 0
|
||||||
|
and item.nc == 0
|
||||||
|
and item.display is None
|
||||||
|
and item.ageing is None
|
||||||
|
)
|
||||||
|
old = (
|
||||||
|
db.execute(
|
||||||
|
select(MozimoStockRegister).where(
|
||||||
|
MozimoStockRegister.sku_id == item.product.id_,
|
||||||
|
MozimoStockRegister.date_ == d,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.scalars()
|
||||||
|
.one_or_none()
|
||||||
|
)
|
||||||
|
if old is not None:
|
||||||
|
if is_blank:
|
||||||
|
db.delete(old)
|
||||||
|
elif (
|
||||||
|
old.received != item.received
|
||||||
|
or old.sale != item.sale
|
||||||
|
or old.nc != item.nc
|
||||||
|
or old.display != item.display
|
||||||
|
or old.ageing != item.ageing
|
||||||
|
):
|
||||||
|
old.received = item.received
|
||||||
|
old.sale = item.sale
|
||||||
|
old.nc = item.nc
|
||||||
|
old.display = item.display
|
||||||
|
old.ageing = item.ageing
|
||||||
|
old.last_edit_date = now
|
||||||
|
elif not is_blank:
|
||||||
|
entry = MozimoStockRegister(
|
||||||
|
d, item.received, item.sale, item.nc, item.display, item.ageing, item.product.id_
|
||||||
|
)
|
||||||
|
db.add(entry)
|
||||||
|
|
||||||
|
body = build_report(d, db)
|
||||||
|
db.commit()
|
||||||
|
return schemas.MozimoDailyRegister(
|
||||||
|
date_=d,
|
||||||
|
body=body,
|
||||||
|
)
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=str(e),
|
||||||
|
)
|
||||||
@ -4,7 +4,6 @@ from datetime import UTC, date, datetime
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Request, Security, status
|
from fastapi import APIRouter, HTTPException, Request, Security, status
|
||||||
from sqlalchemy import desc
|
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy.sql.expression import select
|
from sqlalchemy.sql.expression import select
|
||||||
@ -18,6 +17,7 @@ from ...models.mozimo_stock_register import MozimoStockRegister
|
|||||||
from ...models.stock_keeping_unit import StockKeepingUnit
|
from ...models.stock_keeping_unit import StockKeepingUnit
|
||||||
from ...schemas.user import UserToken
|
from ...schemas.user import UserToken
|
||||||
from ..attendance import date_range
|
from ..attendance import date_range
|
||||||
|
from .mozimo_daily_register import opening_balance
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -26,7 +26,7 @@ router = APIRouter()
|
|||||||
@router.get("", response_model=schemas.MozimoProductRegister)
|
@router.get("", response_model=schemas.MozimoProductRegister)
|
||||||
def show_blank(
|
def show_blank(
|
||||||
request: Request,
|
request: Request,
|
||||||
user: UserToken = Security(get_user, scopes=["ledger"]),
|
user: UserToken = Security(get_user, scopes=["product-ledger"]),
|
||||||
) -> schemas.MozimoProductRegister:
|
) -> schemas.MozimoProductRegister:
|
||||||
return schemas.MozimoProductRegister(
|
return schemas.MozimoProductRegister(
|
||||||
start_date=get_start_date(request.session),
|
start_date=get_start_date(request.session),
|
||||||
@ -42,7 +42,7 @@ def show_data(
|
|||||||
request: Request,
|
request: Request,
|
||||||
s: str | None = None,
|
s: str | None = None,
|
||||||
f: str | None = None,
|
f: str | None = None,
|
||||||
user: UserToken = Security(get_user, scopes=["ledger"]),
|
user: UserToken = Security(get_user, scopes=["product-ledger"]),
|
||||||
) -> schemas.MozimoProductRegister:
|
) -> schemas.MozimoProductRegister:
|
||||||
with SessionFuture() as db:
|
with SessionFuture() as db:
|
||||||
sku = db.execute(select(StockKeepingUnit).where(StockKeepingUnit.id == id_)).scalar_one()
|
sku = db.execute(select(StockKeepingUnit).where(StockKeepingUnit.id == id_)).scalar_one()
|
||||||
@ -98,23 +98,11 @@ def build_report(
|
|||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
def opening_balance(product_id: uuid.UUID, start_date: date, db: Session) -> Decimal:
|
|
||||||
opening = db.execute(
|
|
||||||
select(MozimoStockRegister.display + MozimoStockRegister.ageing)
|
|
||||||
.order_by(desc(MozimoStockRegister.date_))
|
|
||||||
.where(
|
|
||||||
MozimoStockRegister.sku_id == product_id,
|
|
||||||
MozimoStockRegister.date_ < start_date,
|
|
||||||
)
|
|
||||||
).scalar()
|
|
||||||
return Decimal(0) if opening is None else opening
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("", response_model=schemas.MozimoProductRegister)
|
@router.post("", response_model=schemas.MozimoProductRegister)
|
||||||
def save_route(
|
def save_route(
|
||||||
request: Request,
|
request: Request,
|
||||||
data: schemas.MozimoProductRegister,
|
data: schemas.MozimoProductRegister,
|
||||||
user: UserToken = Security(get_user, scopes=["ledger"]),
|
user: UserToken = Security(get_user, scopes=["product-ledger"]),
|
||||||
) -> schemas.MozimoProductRegister:
|
) -> schemas.MozimoProductRegister:
|
||||||
try:
|
try:
|
||||||
if any(i for i in data.body if i.received < 0):
|
if any(i for i in data.body if i.received < 0):
|
||||||
@ -158,6 +146,13 @@ def save_route(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for item in data.body:
|
for item in data.body:
|
||||||
|
is_blank = (
|
||||||
|
item.received == 0
|
||||||
|
and item.sale == 0
|
||||||
|
and item.nc == 0
|
||||||
|
and item.display is None
|
||||||
|
and item.ageing is None
|
||||||
|
)
|
||||||
old = (
|
old = (
|
||||||
db.execute(
|
db.execute(
|
||||||
select(MozimoStockRegister).where(
|
select(MozimoStockRegister).where(
|
||||||
@ -169,7 +164,9 @@ def save_route(
|
|||||||
.one_or_none()
|
.one_or_none()
|
||||||
)
|
)
|
||||||
if old is not None:
|
if old is not None:
|
||||||
if (
|
if is_blank:
|
||||||
|
db.delete(old)
|
||||||
|
elif (
|
||||||
old.received != item.received
|
old.received != item.received
|
||||||
or old.sale != item.sale
|
or old.sale != item.sale
|
||||||
or old.nc != item.nc
|
or old.nc != item.nc
|
||||||
@ -182,7 +179,7 @@ def save_route(
|
|||||||
old.display = item.display
|
old.display = item.display
|
||||||
old.ageing = item.ageing
|
old.ageing = item.ageing
|
||||||
old.last_edit_date = now
|
old.last_edit_date = now
|
||||||
else:
|
elif not is_blank:
|
||||||
entry = MozimoStockRegister(
|
entry = MozimoStockRegister(
|
||||||
item.date_, item.received, item.sale, item.nc, item.display, item.ageing, data.product.id_
|
item.date_, item.received, item.sale, item.nc, item.display, item.ageing, data.product.id_
|
||||||
)
|
)
|
||||||
|
|||||||
57
brewman/brewman/schemas/mozimo_daily_register.py
Normal file
57
brewman/brewman/schemas/mozimo_daily_register.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from pydantic import (
|
||||||
|
BaseModel,
|
||||||
|
ConfigDict,
|
||||||
|
FieldSerializationInfo,
|
||||||
|
field_serializer,
|
||||||
|
field_validator,
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import Daf, to_camel
|
||||||
|
from .product import ProductLink
|
||||||
|
|
||||||
|
|
||||||
|
class MozimoDailyRegisterItem(BaseModel):
|
||||||
|
id_: uuid.UUID | None = None
|
||||||
|
product: ProductLink
|
||||||
|
opening: Daf
|
||||||
|
received: Daf
|
||||||
|
sale: Daf
|
||||||
|
nc: Daf
|
||||||
|
display: Daf | None
|
||||||
|
ageing: Daf | None
|
||||||
|
last_edit_date: datetime | None = None
|
||||||
|
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
|
||||||
|
|
||||||
|
@field_validator("last_edit_date", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None:
|
||||||
|
if value is None or value == "":
|
||||||
|
return None
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value
|
||||||
|
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||||
|
|
||||||
|
@field_serializer("last_edit_date")
|
||||||
|
def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str | None:
|
||||||
|
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||||
|
|
||||||
|
|
||||||
|
class MozimoDailyRegister(BaseModel):
|
||||||
|
date_: date
|
||||||
|
body: list[MozimoDailyRegisterItem]
|
||||||
|
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||||
|
|
||||||
|
@field_validator("date_", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def parse_date(cls, value: date | str) -> date:
|
||||||
|
if isinstance(value, date):
|
||||||
|
return value
|
||||||
|
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||||
|
|
||||||
|
@field_serializer("date_")
|
||||||
|
def serialize_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||||
|
return value.strftime("%d-%b-%Y")
|
||||||
@ -89,6 +89,10 @@ export const routes: Routes = [
|
|||||||
path: 'ledger',
|
path: 'ledger',
|
||||||
loadChildren: () => import('./ledger/ledger.routes').then((mod) => mod.routes),
|
loadChildren: () => import('./ledger/ledger.routes').then((mod) => mod.routes),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'mozimo-daily-register',
|
||||||
|
loadChildren: () => import('./mozimo-daily-register/mozimo-daily-register.routes').then((mod) => mod.routes),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'mozimo-product-register',
|
path: 'mozimo-product-register',
|
||||||
loadChildren: () => import('./mozimo-product-register/mozimo-product-register.routes').then((mod) => mod.routes),
|
loadChildren: () => import('./mozimo-product-register/mozimo-product-register.routes').then((mod) => mod.routes),
|
||||||
|
|||||||
@ -36,6 +36,7 @@
|
|||||||
<a mat-menu-item routerLink="/batch-integrity-report">Batch Integrity</a>
|
<a mat-menu-item routerLink="/batch-integrity-report">Batch Integrity</a>
|
||||||
<a mat-menu-item routerLink="/non-contract-purchase">Non Contract Purchases</a>
|
<a mat-menu-item routerLink="/non-contract-purchase">Non Contract Purchases</a>
|
||||||
<a mat-menu-item routerLink="/mozimo-product-register">Mozimo Product Register</a>
|
<a mat-menu-item routerLink="/mozimo-product-register">Mozimo Product Register</a>
|
||||||
|
<a mat-menu-item routerLink="/mozimo-daily-register">Mozimo Daily Register</a>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<button mat-button [matMenuTriggerFor]="productReportMenu">Product Reports</button>
|
<button mat-button [matMenuTriggerFor]="productReportMenu">Product Reports</button>
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { DataSource } from '@angular/cdk/collections';
|
||||||
|
import { EventEmitter } from '@angular/core';
|
||||||
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
|
import { merge, Observable } from 'rxjs';
|
||||||
|
import { map, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { MozimoDailyRegisterItem } from './mozimo-daily-register-item';
|
||||||
|
|
||||||
|
export class MozimoDailyRegisterDataSource extends DataSource<MozimoDailyRegisterItem> {
|
||||||
|
public data: MozimoDailyRegisterItem[] = [];
|
||||||
|
public paginator?: MatPaginator;
|
||||||
|
constructor(public dataObs: Observable<MozimoDailyRegisterItem[]>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(): Observable<MozimoDailyRegisterItem[]> {
|
||||||
|
const dataMutations: EventEmitter<PageEvent>[] = [];
|
||||||
|
const d = this.dataObs.pipe(
|
||||||
|
tap((x) => {
|
||||||
|
this.data = x;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (this.paginator) {
|
||||||
|
dataMutations.push((this.paginator as MatPaginator).page);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merge(d, ...dataMutations).pipe(
|
||||||
|
map(() => this.calculate([...this.data])),
|
||||||
|
tap(() => {
|
||||||
|
if (this.paginator) {
|
||||||
|
this.paginator.length = this.data.length;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
map((x: MozimoDailyRegisterItem[]) => this.getPagedData(x)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {}
|
||||||
|
|
||||||
|
private calculate(data: MozimoDailyRegisterItem[]): MozimoDailyRegisterItem[] {
|
||||||
|
if (data.length === 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (item.ageing !== null || item.display !== null) {
|
||||||
|
item.closing = (item.ageing ?? 0) + (item.display ?? 0);
|
||||||
|
item.variance = item.opening + item.received - item.sale - item.nc - item.closing;
|
||||||
|
} else {
|
||||||
|
item.closing = item.opening + item.received - item.sale - item.nc;
|
||||||
|
item.variance = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPagedData(data: MozimoDailyRegisterItem[]) {
|
||||||
|
if (this.paginator === undefined) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
|
||||||
|
return data.splice(startIndex, this.paginator.pageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { Product } from '../core/product';
|
||||||
|
|
||||||
|
export class MozimoDailyRegisterItem {
|
||||||
|
id: string | null;
|
||||||
|
product: Product;
|
||||||
|
opening: number;
|
||||||
|
received: number;
|
||||||
|
sale: number;
|
||||||
|
nc: number;
|
||||||
|
display: number | null;
|
||||||
|
ageing: number | null;
|
||||||
|
variance: number | null;
|
||||||
|
closing: number;
|
||||||
|
lastEditDate: string | null;
|
||||||
|
|
||||||
|
public constructor(init?: Partial<MozimoDailyRegisterItem>) {
|
||||||
|
this.id = null;
|
||||||
|
this.product = new Product();
|
||||||
|
this.opening = 0;
|
||||||
|
this.received = 0;
|
||||||
|
this.sale = 0;
|
||||||
|
this.nc = 0;
|
||||||
|
this.display = null;
|
||||||
|
this.ageing = null;
|
||||||
|
this.variance = null;
|
||||||
|
this.closing = 0;
|
||||||
|
this.lastEditDate = null;
|
||||||
|
Object.assign(this, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle {
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.last {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title-group>
|
||||||
|
<mat-card-title>Mozimo Product Register</mat-card-title>
|
||||||
|
@if (dataSource.data.length) {
|
||||||
|
<button mat-icon-button (click)="exportCsv()">
|
||||||
|
<mat-icon>save_alt</mat-icon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</mat-card-title-group>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<form [formGroup]="form" class="flex flex-col">
|
||||||
|
<div class="flex flex-row justify-around content-start items-start sm:max-lg:flex-col">
|
||||||
|
<mat-form-field class="flex-auto basis-4/5 mr-5">
|
||||||
|
<mat-label>Date</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
#dateElement
|
||||||
|
[matDatepicker]="date"
|
||||||
|
(focus)="dateElement.select()"
|
||||||
|
formControlName="date"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
|
||||||
|
<mat-datepicker #date></mat-datepicker>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-raised-button class="flex-auto basis-1/5" color="primary" (click)="show()">Show</button>
|
||||||
|
</div>
|
||||||
|
<mat-table #table [dataSource]="dataSource" aria-label="Elements" formArrayName="items">
|
||||||
|
<!-- Product Column -->
|
||||||
|
<ng-container matColumnDef="product">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="center first">Product</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="center first">
|
||||||
|
<span
|
||||||
|
matBadge="1"
|
||||||
|
matBadgeSize="small"
|
||||||
|
[matBadgeHidden]="!row.lastEditDate"
|
||||||
|
matTooltip="{{ row.lastEditDate | localTime }}"
|
||||||
|
[matTooltipDisabled]="!row.lastEditDate"
|
||||||
|
>{{ row.product.name }}</span
|
||||||
|
>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Opening Column -->
|
||||||
|
<ng-container matColumnDef="opening">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="right middle">Opening</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="right middle">{{ row.opening | number: '0.2-2' }}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Received Column -->
|
||||||
|
<ng-container matColumnDef="received">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="middle">Received</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row; let i = index" [formGroupName]="i" class="middle">
|
||||||
|
<mat-form-field class="flex-auto">
|
||||||
|
<mat-label>Received</mat-label>
|
||||||
|
<input matInput type="number" formControlName="received" (change)="updateReceived($event, row)" />
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Sale Column -->
|
||||||
|
<ng-container matColumnDef="sale">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="middle">Sale</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row; let i = index" [formGroupName]="i" class="middle">
|
||||||
|
<mat-form-field class="flex-auto">
|
||||||
|
<mat-label>Sale</mat-label>
|
||||||
|
<input matInput type="number" formControlName="sale" (change)="updateSale($event, row)" />
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Nc Column -->
|
||||||
|
<ng-container matColumnDef="nc">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="middle">Nc</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row; let i = index" [formGroupName]="i" class="middle">
|
||||||
|
<mat-form-field class="flex-auto">
|
||||||
|
<mat-label>Nc</mat-label>
|
||||||
|
<input matInput type="number" formControlName="nc" (change)="updateNc($event, row)" />
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Display Column -->
|
||||||
|
<ng-container matColumnDef="display">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="middle">Display</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row; let i = index" [formGroupName]="i" class="middle">
|
||||||
|
<mat-form-field class="flex-auto">
|
||||||
|
<mat-label>Display</mat-label>
|
||||||
|
<input matInput type="number" formControlName="display" (change)="updateDisplay($event, row)" />
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Ageing Column -->
|
||||||
|
<ng-container matColumnDef="ageing">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="middle">Ageing</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row; let i = index" [formGroupName]="i" class="middle">
|
||||||
|
<mat-form-field class="flex-auto">
|
||||||
|
<mat-label>Ageing</mat-label>
|
||||||
|
<input matInput type="number" formControlName="ageing" (change)="updateAgeing($event, row)" />
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Variance Column -->
|
||||||
|
<ng-container matColumnDef="variance">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="right middle">Variance</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="right middle">{{ row.variance | number: '0.2-2' }}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Closing Column -->
|
||||||
|
<ng-container matColumnDef="closing">
|
||||||
|
<mat-header-cell *matHeaderCellDef class="right last">Closing</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let row" class="right last">{{ row.closing | number: '0.2-2' }}</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||||
|
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||||
|
</mat-table>
|
||||||
|
|
||||||
|
<mat-paginator
|
||||||
|
#paginator
|
||||||
|
[length]="dataSource.data.length"
|
||||||
|
[pageIndex]="0"
|
||||||
|
[pageSize]="50"
|
||||||
|
[pageSizeOptions]="[25, 50, 100, 250, 300, 5000]"
|
||||||
|
>
|
||||||
|
</mat-paginator>
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-raised-button color="primary" (click)="save()" [disabled]="form.pristine">Save</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MozimoDailyRegisterComponent } from './mozimo-daily-register.component';
|
||||||
|
|
||||||
|
describe('MozimoProductRegisterComponent', () => {
|
||||||
|
let component: MozimoDailyRegisterComponent;
|
||||||
|
let fixture: ComponentFixture<MozimoDailyRegisterComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MozimoDailyRegisterComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MozimoDailyRegisterComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,223 @@
|
|||||||
|
import { DecimalPipe, CurrencyPipe, AsyncPipe } from '@angular/common';
|
||||||
|
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { FormGroup, FormArray, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
import { MatBadgeModule } from '@angular/material/badge';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
|
||||||
|
import { MatTableModule } from '@angular/material/table';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import { AuthService } from '../auth/auth.service';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { ToCsvService } from '../shared/to-csv.service';
|
||||||
|
|
||||||
|
import { MozimoDailyRegister } from './mozimo-daily-register';
|
||||||
|
import { MozimoDailyRegisterDataSource } from './mozimo-daily-register-datasource';
|
||||||
|
import { MozimoDailyRegisterItem } from './mozimo-daily-register-item';
|
||||||
|
import { MozimoDailyRegisterService } from './mozimo-daily-register.service';
|
||||||
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { LocalTimePipe } from '../shared/local-time.pipe';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-mozimo-daily-register',
|
||||||
|
templateUrl: './mozimo-daily-register.component.html',
|
||||||
|
styleUrls: ['./mozimo-daily-register.component.css'],
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
AsyncPipe,
|
||||||
|
CurrencyPipe,
|
||||||
|
DecimalPipe,
|
||||||
|
LocalTimePipe,
|
||||||
|
MatAutocompleteModule,
|
||||||
|
MatBadgeModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatDatepickerModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
],
|
||||||
|
providers: [LocalTimePipe],
|
||||||
|
})
|
||||||
|
export class MozimoDailyRegisterComponent implements OnInit {
|
||||||
|
@ViewChild('dateElement', { static: false }) dateElement!: ElementRef<HTMLInputElement>;
|
||||||
|
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
|
||||||
|
info: MozimoDailyRegister = new MozimoDailyRegister();
|
||||||
|
body = new BehaviorSubject<MozimoDailyRegisterItem[]>([]);
|
||||||
|
dataSource: MozimoDailyRegisterDataSource = new MozimoDailyRegisterDataSource(this.body);
|
||||||
|
|
||||||
|
form: FormGroup<{
|
||||||
|
date: FormControl<Date>;
|
||||||
|
items: FormArray<
|
||||||
|
FormGroup<{
|
||||||
|
received: FormControl<number>;
|
||||||
|
sale: FormControl<number>;
|
||||||
|
nc: FormControl<number>;
|
||||||
|
display: FormControl<number | null>;
|
||||||
|
ageing: FormControl<number | null>;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
|
}>;
|
||||||
|
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||||
|
|
||||||
|
displayedColumns = ['product', 'opening', 'received', 'sale', 'nc', 'display', 'ageing', 'variance', 'closing'];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private toCsv: ToCsvService,
|
||||||
|
private localTimePipe: LocalTimePipe,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
public auth: AuthService,
|
||||||
|
private ser: MozimoDailyRegisterService,
|
||||||
|
) {
|
||||||
|
this.form = new FormGroup({
|
||||||
|
date: new FormControl(new Date(), { nonNullable: true }),
|
||||||
|
items: new FormArray<
|
||||||
|
FormGroup<{
|
||||||
|
received: FormControl<number>;
|
||||||
|
sale: FormControl<number>;
|
||||||
|
nc: FormControl<number>;
|
||||||
|
display: FormControl<number | null>;
|
||||||
|
ageing: FormControl<number | null>;
|
||||||
|
}>
|
||||||
|
>([]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.data.subscribe((value) => {
|
||||||
|
const data = value as { info: MozimoDailyRegister };
|
||||||
|
this.info = data.info;
|
||||||
|
this.form.patchValue({
|
||||||
|
date: moment(this.info.date, 'DD-MMM-YYYY').toDate(),
|
||||||
|
});
|
||||||
|
this.form.controls.items.clear();
|
||||||
|
this.info.body.forEach((x) =>
|
||||||
|
this.form.controls.items.push(
|
||||||
|
new FormGroup({
|
||||||
|
received: new FormControl(x.received, { nonNullable: true, validators: [Validators.min(0)] }),
|
||||||
|
sale: new FormControl(x.sale, { nonNullable: true, validators: [Validators.min(0)] }),
|
||||||
|
nc: new FormControl(x.nc, { nonNullable: true, validators: [Validators.min(0)] }),
|
||||||
|
display: new FormControl(x.display, { nonNullable: false, validators: [Validators.min(0)] }),
|
||||||
|
ageing: new FormControl(x.ageing, { nonNullable: false, validators: [Validators.min(0)] }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!this.dataSource.paginator) {
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
}
|
||||||
|
this.body.next(this.info.body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
const date = moment(this.form.value.date).format('DD-MMM-YYYY');
|
||||||
|
this.router.navigate(['/mozimo-daily-register', date]);
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.ser.save(this.getMozimoDailyRegister()).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.snackBar.open('', 'Success');
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.snackBar.open(error, 'Danger');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getMozimoDailyRegister(): MozimoDailyRegister {
|
||||||
|
const formModel = this.form.value;
|
||||||
|
this.info.date = moment(formModel.date).format('DD-MMM-YYYY');
|
||||||
|
|
||||||
|
const array = this.form.controls.items;
|
||||||
|
this.info.body.forEach((item, index) => {
|
||||||
|
item.received = +(array.controls[index].value.received ?? 0);
|
||||||
|
item.sale = +(array.controls[index].value.sale ?? 0);
|
||||||
|
item.nc = +(array.controls[index].value.nc ?? 0);
|
||||||
|
const display = array.controls[index].value.display ?? null;
|
||||||
|
const ageing = array.controls[index].value.ageing ?? null;
|
||||||
|
item.display = display == null ? null : +display;
|
||||||
|
item.ageing = ageing == null ? null : +ageing;
|
||||||
|
});
|
||||||
|
return this.info;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportCsv() {
|
||||||
|
const headers = {
|
||||||
|
Product: 'product',
|
||||||
|
Opening: 'opening',
|
||||||
|
Received: 'received',
|
||||||
|
Sale: 'sale',
|
||||||
|
Nc: 'nc',
|
||||||
|
Display: 'display',
|
||||||
|
Ageing: 'ageing',
|
||||||
|
Variance: 'variance',
|
||||||
|
Closing: 'closing',
|
||||||
|
'Last Edit Date': 'lastEditDate',
|
||||||
|
};
|
||||||
|
const d = JSON.parse(JSON.stringify(this.dataSource.data)).map((x: MozimoDailyRegisterItem) => ({
|
||||||
|
id: x.id,
|
||||||
|
product: x.product.name,
|
||||||
|
opening: x.opening,
|
||||||
|
received: x.received,
|
||||||
|
sale: x.sale,
|
||||||
|
nc: x.nc,
|
||||||
|
display: x.display,
|
||||||
|
ageing: x.ageing,
|
||||||
|
variance: x.variance,
|
||||||
|
closing: x.closing,
|
||||||
|
lastEditDate: x.lastEditDate ? this.localTimePipe.transform(x.lastEditDate ?? '') : 'Unsaved',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const csvData = new Blob([this.toCsv.toCsv(headers, d)], {
|
||||||
|
type: 'text/csv;charset=utf-8;',
|
||||||
|
});
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = window.URL.createObjectURL(csvData);
|
||||||
|
link.setAttribute('download', 'mozimo-product-register.csv');
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateReceived($event: Event, row: MozimoDailyRegisterItem) {
|
||||||
|
row.received = +($event.target as HTMLInputElement).value;
|
||||||
|
this.body.next(this.info.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSale($event: Event, row: MozimoDailyRegisterItem) {
|
||||||
|
row.sale = +($event.target as HTMLInputElement).value;
|
||||||
|
this.body.next(this.info.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNc($event: Event, row: MozimoDailyRegisterItem) {
|
||||||
|
row.nc = +($event.target as HTMLInputElement).value;
|
||||||
|
this.body.next(this.info.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDisplay($event: Event, row: MozimoDailyRegisterItem) {
|
||||||
|
const val = ($event.target as HTMLInputElement).value;
|
||||||
|
row.display = val === '' ? null : +val;
|
||||||
|
this.body.next(this.info.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAgeing($event: Event, row: MozimoDailyRegisterItem) {
|
||||||
|
const val = ($event.target as HTMLInputElement).value;
|
||||||
|
row.ageing = val === '' ? null : +val;
|
||||||
|
this.body.next(this.info.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { ResolveFn } from '@angular/router';
|
||||||
|
|
||||||
|
import { MozimoDailyRegister } from './mozimo-daily-register';
|
||||||
|
import { mozimoDailyRegisterResolver } from './mozimo-daily-register.resolver';
|
||||||
|
|
||||||
|
describe('mozimoProductRegisterResolver', () => {
|
||||||
|
const executeResolver: ResolveFn<MozimoDailyRegister> = (...resolverParameters) =>
|
||||||
|
TestBed.runInInjectionContext(() => mozimoDailyRegisterResolver(...resolverParameters));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(executeResolver).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { inject } from '@angular/core';
|
||||||
|
import { ResolveFn } from '@angular/router';
|
||||||
|
|
||||||
|
import { MozimoDailyRegister } from './mozimo-daily-register';
|
||||||
|
import { MozimoDailyRegisterService } from './mozimo-daily-register.service';
|
||||||
|
|
||||||
|
export const mozimoDailyRegisterResolver: ResolveFn<MozimoDailyRegister> = (route) => {
|
||||||
|
const date = route.paramMap.get('date');
|
||||||
|
return inject(MozimoDailyRegisterService).list(date);
|
||||||
|
};
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { authGuard } from '../auth/auth-guard.service';
|
||||||
|
|
||||||
|
import { MozimoDailyRegisterComponent } from './mozimo-daily-register.component';
|
||||||
|
import { mozimoDailyRegisterResolver } from './mozimo-daily-register.resolver';
|
||||||
|
|
||||||
|
export const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: MozimoDailyRegisterComponent,
|
||||||
|
canActivate: [authGuard],
|
||||||
|
data: {
|
||||||
|
permission: 'Product Ledger',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
info: mozimoDailyRegisterResolver,
|
||||||
|
},
|
||||||
|
runGuardsAndResolvers: 'always',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':date',
|
||||||
|
component: MozimoDailyRegisterComponent,
|
||||||
|
canActivate: [authGuard],
|
||||||
|
data: {
|
||||||
|
permission: 'Product Ledger',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
info: mozimoDailyRegisterResolver,
|
||||||
|
},
|
||||||
|
runGuardsAndResolvers: 'always',
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
||||||
|
import { inject, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MozimoDailyRegisterService } from './mozimo-daily-register.service';
|
||||||
|
|
||||||
|
describe('MozimoProductRegisterService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [MozimoDailyRegisterService, provideHttpClient(withInterceptorsFromDi())],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([MozimoDailyRegisterService], (service: MozimoDailyRegisterService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { ErrorLoggerService } from '../core/error-logger.service';
|
||||||
|
|
||||||
|
import { MozimoDailyRegister } from './mozimo-daily-register';
|
||||||
|
|
||||||
|
const url = '/api/mozimo-daily-register';
|
||||||
|
const serviceName = 'MozimoDailyRegisterService';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class MozimoDailyRegisterService {
|
||||||
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
private log: ErrorLoggerService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
list(date: string | null): Observable<MozimoDailyRegister> {
|
||||||
|
const listUrl: string = date === null ? url : `${url}/${date}`;
|
||||||
|
return this.http
|
||||||
|
.get<MozimoDailyRegister>(listUrl)
|
||||||
|
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<MozimoDailyRegister>;
|
||||||
|
}
|
||||||
|
|
||||||
|
save(mozimoProductRegister: MozimoDailyRegister): Observable<MozimoDailyRegister> {
|
||||||
|
return this.http
|
||||||
|
.post<MozimoDailyRegister>(`${url}/${mozimoProductRegister.date}`, mozimoProductRegister)
|
||||||
|
.pipe(catchError(this.log.handleError(serviceName, 'save'))) as Observable<MozimoDailyRegister>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { MozimoDailyRegisterItem } from './mozimo-daily-register-item';
|
||||||
|
|
||||||
|
export class MozimoDailyRegister {
|
||||||
|
date: string;
|
||||||
|
body: MozimoDailyRegisterItem[];
|
||||||
|
|
||||||
|
public constructor(init?: Partial<MozimoDailyRegister>) {
|
||||||
|
this.date = '';
|
||||||
|
this.body = [];
|
||||||
|
Object.assign(this, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@ export const routes: Routes = [
|
|||||||
component: MozimoProductRegisterComponent,
|
component: MozimoProductRegisterComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: {
|
data: {
|
||||||
permission: 'Ledger',
|
permission: 'Product Ledger',
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
info: mozimoProductRegisterResolver,
|
info: mozimoProductRegisterResolver,
|
||||||
@ -23,8 +23,7 @@ export const routes: Routes = [
|
|||||||
component: MozimoProductRegisterComponent,
|
component: MozimoProductRegisterComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: {
|
data: {
|
||||||
// permission: 'Mozimo Product Register',
|
permission: 'Product Ledger',
|
||||||
permission: 'Ledger',
|
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
info: mozimoProductRegisterResolver,
|
info: mozimoProductRegisterResolver,
|
||||||
|
|||||||
Reference in New Issue
Block a user