Feature: Added sale voucher to check sale import.
Chore: Frontend moved from Moment to date-fns and from dd-MMM-yyyy to ISO Date format.
This commit is contained in:
1 parent
7656c7e877
commit
fc3cd00662
102 files changed
+2121
-2103
No files matched your search
@@ -45,6 +45,7 @@ from .routers import (
|
||||
rebase,
|
||||
recipe,
|
||||
role,
|
||||
sale,
|
||||
sales_import,
|
||||
tag,
|
||||
temporal_product,
|
||||
@@ -165,6 +166,7 @@ app.include_router(credit_salary.router, prefix="/api/credit-salary", tags=["vou
|
||||
app.include_router(voucher.router, prefix="/api", tags=["vouchers"])
|
||||
app.include_router(transaction_import.router, prefix="/api/transaction-import", tags=["vouchers"])
|
||||
app.include_router(sales_import.router, prefix="/api/sales-import", tags=["vouchers"])
|
||||
app.include_router(sale.router, prefix="/api/sale", tags=["vouchers"])
|
||||
|
||||
app.include_router(lock_information.router, prefix="/api/lock-information", tags=["settings"])
|
||||
app.include_router(maintenance.router, prefix="/api/maintenance", tags=["settings"])
|
||||
|
||||
@@ -86,7 +86,7 @@ def effective_date(d: str | None = None) -> date:
|
||||
datetime.now(UTC).replace(tzinfo=None) + timedelta(minutes=330) # Adjust for IST (Indian Standard Time)
|
||||
).date()
|
||||
if d is None
|
||||
else datetime.strptime(d, "%d-%b-%Y").date()
|
||||
else date.fromisoformat(d)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
@@ -142,10 +142,9 @@ async def show_balance(
|
||||
id_: uuid.UUID,
|
||||
user: Annotated[UserToken, Security(get_user)],
|
||||
db: SessionDep,
|
||||
d: str | None = None,
|
||||
d: date | None = None,
|
||||
) -> AccountBalance:
|
||||
date_ = None if d is None or d == "" else datetime.strptime(d, "%d-%b-%Y")
|
||||
return AccountBalance(date_=balance(id_, date_, db), total=balance(id_, None, db))
|
||||
return AccountBalance(date_=balance(id_, d, db), total=balance(id_, None, db))
|
||||
|
||||
|
||||
@router.get("/{id_}", response_model=schemas.Account)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import datetime
|
||||
|
||||
from datetime import date
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Security
|
||||
@@ -24,18 +23,17 @@ router = APIRouter()
|
||||
|
||||
@router.get("", response_model=list[schemas.Batch])
|
||||
def batch_term(
|
||||
q: str | None, d: str, user: Annotated[UserToken, Security(get_user)], db: SessionDep
|
||||
q: str | None, d: date, user: Annotated[UserToken, Security(get_user)], db: SessionDep
|
||||
) -> list[schemas.Batch]:
|
||||
date_ = datetime.datetime.strptime(d, "%d-%b-%Y")
|
||||
list_: list[schemas.Batch] = []
|
||||
|
||||
query = (
|
||||
select(Batch)
|
||||
.join(Batch.sku)
|
||||
.join(StockKeepingUnit.product)
|
||||
.join(ProductVersion, onclause=_pv_onclause(date_))
|
||||
.join(SkuVersion, onclause=_sv_onclause(date_))
|
||||
.where(Batch.quantity_remaining > 0, Batch.name <= date_)
|
||||
.join(ProductVersion, onclause=_pv_onclause(d))
|
||||
.join(SkuVersion, onclause=_sv_onclause(d))
|
||||
.where(Batch.quantity_remaining > 0, Batch.name <= d)
|
||||
.options(
|
||||
contains_eager(Batch.sku).contains_eager(StockKeepingUnit.product).contains_eager(Product.versions),
|
||||
contains_eager(Batch.sku).contains_eager(StockKeepingUnit.versions),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from calendar import monthrange
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
@@ -28,11 +28,10 @@ router = APIRouter()
|
||||
def credit_salary(
|
||||
user: Annotated[UserToken, Security(get_user, scopes=["attendance"])],
|
||||
db: SessionDep,
|
||||
month: str = Body(..., embed=True),
|
||||
month: date = Body(..., embed=True),
|
||||
) -> Message:
|
||||
month_ = datetime.strptime(month, "%d-%b-%Y").date()
|
||||
start_date = get_first_day(month_)
|
||||
finish_date = get_last_day(month_)
|
||||
start_date = get_first_day(month)
|
||||
finish_date = get_last_day(month)
|
||||
voucher = Voucher(
|
||||
date_=finish_date,
|
||||
narration="Auto Generated Salary Entry",
|
||||
|
||||
@@ -82,7 +82,7 @@ def employee_attendance(
|
||||
prints, hours_worked, full_day = get_prints(employee.id, item, db)
|
||||
list_.append(
|
||||
schemas.EmployeeAttendanceItem(
|
||||
date_=item.strftime("%d-%b-%Y"), # type: ignore
|
||||
date_=item,
|
||||
attendance_type=AttendanceTypeSchema(id_=at_type),
|
||||
prints=prints,
|
||||
hours_worked=hours_worked,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import csv
|
||||
import io
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import StreamingResponse
|
||||
@@ -19,19 +19,14 @@ router = APIRouter()
|
||||
|
||||
@router.get("", response_class=StreamingResponse)
|
||||
def get_report(
|
||||
s: str,
|
||||
f: str,
|
||||
s: date,
|
||||
f: date,
|
||||
# user: Annotated[UserToken, Security(get_user, scopes=["attendance"])] ## removed as jwt headers are a pain in the ass
|
||||
db: SessionDep,
|
||||
) -> StreamingResponse:
|
||||
try:
|
||||
output = io.StringIO()
|
||||
attendance_record(
|
||||
datetime.strptime(s, "%d-%b-%Y"),
|
||||
datetime.strptime(f, "%d-%b-%Y"),
|
||||
output,
|
||||
db,
|
||||
)
|
||||
attendance_record(s, f, output, db)
|
||||
headers = {"Content-Disposition": "attachment; filename = fingerprint-record.csv"}
|
||||
output.seek(0)
|
||||
return StreamingResponse(output, media_type="text/csv", headers=headers)
|
||||
|
||||
@@ -45,11 +45,11 @@ async def save(
|
||||
)
|
||||
db.add(item)
|
||||
add_items(item, data.items, db)
|
||||
report_date(request, data.date_.strftime("%d-%b-%Y"))
|
||||
report_date(request, data.date_)
|
||||
if data.valid_from is not None:
|
||||
report_start_date(request, data.valid_from.strftime("%d-%b-%Y"))
|
||||
report_start_date(request, data.valid_from)
|
||||
if data.valid_till is not None:
|
||||
report_finish_date(request, data.valid_till.strftime("%d-%b-%Y"))
|
||||
report_finish_date(request, data.valid_till)
|
||||
db.commit()
|
||||
return rate_contract_info(item, db)
|
||||
|
||||
@@ -77,11 +77,11 @@ async def update_route(
|
||||
item.narration = data.narration
|
||||
item.user_id = user.id_
|
||||
update_items(item, data.items, db)
|
||||
report_date(request, data.date_.strftime("%d-%b-%Y"))
|
||||
report_date(request, data.date_)
|
||||
if data.valid_from is not None:
|
||||
report_start_date(request, data.valid_from.strftime("%d-%b-%Y"))
|
||||
report_start_date(request, data.valid_from)
|
||||
if data.valid_till is not None:
|
||||
report_finish_date(request, data.valid_till.strftime("%d-%b-%Y"))
|
||||
report_finish_date(request, data.valid_till)
|
||||
db.commit()
|
||||
return rate_contract_info(item, db)
|
||||
|
||||
|
||||
@@ -732,10 +732,10 @@ def _parse_import_date(value: str) -> date:
|
||||
return datetime.strptime(value.strip(), fmt).date()
|
||||
except ValueError:
|
||||
continue
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Invalid date; use DD-MMM-YYYY or YYYY-MM-DD",
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Invalid date; use DD-MMM-YYYY or YYYY-MM-DD",
|
||||
)
|
||||
|
||||
|
||||
def _import_options(date_: date, create: str, sale_prices: bool, master_data: bool, tags: bool) -> ImportOptions:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from datetime import date, timedelta
|
||||
|
||||
from fastapi import Request
|
||||
|
||||
@@ -6,40 +6,43 @@ from fastapi import Request
|
||||
def report_start_date(request: Request, s: str | date | None = None) -> date:
|
||||
session = request.session
|
||||
if isinstance(s, date):
|
||||
session["start"] = s.strftime("%d-%b-%Y")
|
||||
session["start"] = s.isoformat()
|
||||
return s
|
||||
if s:
|
||||
session["start"] = s
|
||||
return datetime.strptime(s, "%d-%b-%Y").date()
|
||||
d = date.fromisoformat(s)
|
||||
session["start"] = d.isoformat()
|
||||
return d
|
||||
if "start" not in session:
|
||||
session["start"] = get_first_day(date.today()).strftime("%d-%b-%Y")
|
||||
return datetime.strptime(session["start"], "%d-%b-%Y").date()
|
||||
session["start"] = get_first_day(date.today()).isoformat()
|
||||
return date.fromisoformat(session["start"])
|
||||
|
||||
|
||||
def report_finish_date(request: Request, f: str | date | None = None) -> date:
|
||||
session = request.session
|
||||
if isinstance(f, date):
|
||||
session["finish"] = f.strftime("%d-%b-%Y")
|
||||
session["finish"] = f.isoformat()
|
||||
return f
|
||||
if f:
|
||||
session["finish"] = f
|
||||
return datetime.strptime(f, "%d-%b-%Y").date()
|
||||
d = date.fromisoformat(f)
|
||||
session["finish"] = d.isoformat()
|
||||
return d
|
||||
if "finish" not in session:
|
||||
session["finish"] = get_last_day(date.today()).strftime("%d-%b-%Y")
|
||||
return datetime.strptime(session["finish"], "%d-%b-%Y").date()
|
||||
session["finish"] = get_last_day(date.today()).isoformat()
|
||||
return date.fromisoformat(session["finish"])
|
||||
|
||||
|
||||
def report_date(request: Request, d: str | date | None = None) -> date:
|
||||
session = request.session
|
||||
if isinstance(d, date):
|
||||
session["date"] = d.strftime("%d-%b-%Y")
|
||||
session["date"] = d.isoformat()
|
||||
return d
|
||||
if d:
|
||||
session["date"] = d
|
||||
return datetime.strptime(d, "%d-%b-%Y").date()
|
||||
parsed_date = date.fromisoformat(d)
|
||||
session["date"] = parsed_date.isoformat()
|
||||
return parsed_date
|
||||
if "date" not in session:
|
||||
session["date"] = get_last_day(date.today()).strftime("%d-%b-%Y")
|
||||
return datetime.strptime(session["date"], "%d-%b-%Y").date()
|
||||
session["date"] = get_last_day(date.today()).isoformat()
|
||||
return date.fromisoformat(session["date"])
|
||||
|
||||
|
||||
def get_first_day(dt: date, d_years: int = 0, d_months: int = 0) -> date:
|
||||
@@ -56,20 +59,23 @@ def get_last_day(dt: date) -> date:
|
||||
def op_report_start_date(request: Request, s: str | None = None) -> date | None:
|
||||
session = request.session
|
||||
if s:
|
||||
session["start"] = s
|
||||
return datetime.strptime(s, "%d-%b-%Y").date()
|
||||
d = date.fromisoformat(s)
|
||||
session["start"] = d.isoformat()
|
||||
return d
|
||||
return None
|
||||
|
||||
|
||||
def op_report_finish_date(request: Request, f: str | None = None) -> date | None:
|
||||
session = request.session
|
||||
if f:
|
||||
session["finish"] = f
|
||||
return datetime.strptime(f, "%d-%b-%Y").date()
|
||||
d = date.fromisoformat(f)
|
||||
session["finish"] = d.isoformat()
|
||||
return d
|
||||
return None
|
||||
|
||||
|
||||
def path_date(request: Request, date_: str) -> date:
|
||||
session = request.session
|
||||
session["date"] = date_
|
||||
return datetime.strptime(date_, "%d-%b-%Y").date()
|
||||
d = date.fromisoformat(date_)
|
||||
session["date"] = d.isoformat()
|
||||
return d
|
||||
@@ -102,11 +102,11 @@ def build_report(
|
||||
)
|
||||
|
||||
for voucher, inventory, journal, stockKeepingUnit in query:
|
||||
# We are tracking at purchase cost centre, but the journal is of the cost centre so we reverse the debit direction.
|
||||
journal_debit = journal.debit * -1
|
||||
if voucher.voucher_type == VoucherType.SALE:
|
||||
print(journal_debit, " pre")
|
||||
# Else sale will be seen as a debit since
|
||||
journal_debit = -1 * journal_debit
|
||||
print(journal_debit)
|
||||
name = (
|
||||
journal.cost_centre.name
|
||||
if voucher.voucher_type in [VoucherType.ISSUE, VoucherType.CLOSING_STOCK, VoucherType.SALE]
|
||||
@@ -130,9 +130,7 @@ def build_report(
|
||||
url=[
|
||||
"/",
|
||||
voucher.voucher_type.name.replace("_", "-").lower(),
|
||||
str(voucher.id)
|
||||
if voucher.voucher_type != VoucherType.CLOSING_STOCK
|
||||
else voucher.date_.strftime("%d-%b-%Y"),
|
||||
str(voucher.id) if voucher.voucher_type != VoucherType.CLOSING_STOCK else voucher.date_.isoformat(),
|
||||
],
|
||||
type_=voucher.voucher_type.name.replace("_", " ").title(),
|
||||
narration=voucher.narration,
|
||||
|
||||
@@ -35,7 +35,7 @@ def report_data(
|
||||
user: Annotated[UserToken, Security(get_user, scopes=["trial-balance"])],
|
||||
db: SessionDep,
|
||||
) -> schemas.TrialBalance:
|
||||
report_finish_date(request, date_.strftime("%d-%b-%Y"))
|
||||
report_finish_date(request, date_)
|
||||
return schemas.TrialBalance(
|
||||
date_=date_,
|
||||
body=build_report(date_, db),
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Security, status
|
||||
from sqlalchemy import select
|
||||
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionDep
|
||||
from ..models.voucher import Voucher
|
||||
from ..models.voucher_type import VoucherType
|
||||
from ..schemas import sale as schemas
|
||||
from ..schemas import voucher as output
|
||||
from ..schemas.user import UserToken
|
||||
from .reports import report_date
|
||||
from .voucher import voucher_info
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def compute_total_amount(vouchers: list[output.Voucher]) -> Decimal:
|
||||
total = Decimal(0)
|
||||
for v in vouchers:
|
||||
for inv in v.inventories:
|
||||
if inv.amount is not None:
|
||||
total += inv.amount
|
||||
return total
|
||||
|
||||
|
||||
@router.get("/{id_}", response_model=schemas.SaleDayResponse)
|
||||
def get_sale_day_by_id(
|
||||
id_: uuid.UUID,
|
||||
user: Annotated[UserToken, Security(get_user)],
|
||||
db: SessionDep,
|
||||
) -> schemas.SaleDayResponse:
|
||||
voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one_or_none()
|
||||
if voucher is None or voucher.voucher_type != VoucherType.SALE:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Sale voucher not found")
|
||||
vouchers = (
|
||||
db.execute(
|
||||
select(Voucher)
|
||||
.where(Voucher.date_ == voucher.date_, Voucher.voucher_type == VoucherType.SALE)
|
||||
.order_by(Voucher.creation_date)
|
||||
)
|
||||
.scalars()
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
voucher_infos = [voucher_info(v, db) for v in vouchers]
|
||||
return schemas.SaleDayResponse(
|
||||
date_=voucher.date_,
|
||||
selected_voucher_id=id_,
|
||||
total_amount=compute_total_amount(voucher_infos),
|
||||
vouchers=voucher_infos,
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=schemas.SaleDayResponse)
|
||||
def get_sale_day_by_date(
|
||||
d: Annotated[date, Depends(report_date)],
|
||||
user: Annotated[UserToken, Security(get_user)],
|
||||
db: SessionDep,
|
||||
) -> schemas.SaleDayResponse:
|
||||
vouchers = (
|
||||
db.execute(
|
||||
select(Voucher)
|
||||
.where(Voucher.date_ == d, Voucher.voucher_type == VoucherType.SALE)
|
||||
.order_by(Voucher.creation_date)
|
||||
)
|
||||
.scalars()
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
voucher_infos = [voucher_info(v, db) for v in vouchers]
|
||||
return schemas.SaleDayResponse(
|
||||
date_=d,
|
||||
selected_voucher_id=None,
|
||||
total_amount=compute_total_amount(voucher_infos),
|
||||
vouchers=voucher_infos,
|
||||
)
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import to_camel
|
||||
from .attendance_type import AttendanceType
|
||||
@@ -31,14 +25,3 @@ class Attendance(BaseModel):
|
||||
date_: date | None = None
|
||||
body: list[AttendanceItem]
|
||||
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")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -21,18 +15,7 @@ class BalanceSheetItem(BaseModel):
|
||||
|
||||
|
||||
class BalanceSheet(BaseModel):
|
||||
date_: date | str
|
||||
date_: date
|
||||
body: list[BalanceSheetItem]
|
||||
footer: BalanceSheetItem | None = None
|
||||
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")
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -21,17 +15,6 @@ class BatchIntegrityItem(BaseModel):
|
||||
price: Daf
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("date_", mode="before")
|
||||
@classmethod
|
||||
def parse_start_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")
|
||||
|
||||
|
||||
class BatchIntegrity(BaseModel):
|
||||
id_: uuid.UUID
|
||||
@@ -42,14 +25,3 @@ class BatchIntegrity(BaseModel):
|
||||
actual: Daf
|
||||
details: list[BatchIntegrityItem]
|
||||
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")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from ..models.voucher_type import VoucherType
|
||||
from . import to_camel
|
||||
@@ -21,14 +15,3 @@ class BlankVoucherInfo(BaseModel):
|
||||
source: CostCentreLink | None = None
|
||||
destination: CostCentreLink | None = None
|
||||
model_config = ConfigDict(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")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -27,30 +21,8 @@ class CashFlowBody(BaseModel):
|
||||
|
||||
|
||||
class CashFlow(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
body: CashFlowBody
|
||||
footer: list[CashFlowItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -2,13 +2,7 @@ import uuid
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import to_camel
|
||||
|
||||
@@ -25,32 +19,8 @@ class Client(ClientIn):
|
||||
creation_date: datetime
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("creation_date", mode="before")
|
||||
@classmethod
|
||||
def parse_creation_date(cls, value: datetime | str) -> datetime | None:
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("creation_date")
|
||||
def serialize_creation_date(self, value: datetime, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
|
||||
class ClientList(Client):
|
||||
last_user: str
|
||||
last_date: datetime | None = None
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("last_date", mode="before")
|
||||
@classmethod
|
||||
def parse_last_date(cls, value: datetime | str | None) -> 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_date")
|
||||
def serialize_last_date(self, value: datetime | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
@@ -3,13 +3,7 @@ import uuid
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .cost_centre import CostCentreLink
|
||||
@@ -59,40 +53,3 @@ class ClosingStock(BaseModel):
|
||||
posted: bool
|
||||
poster: UserLink | None = None
|
||||
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")
|
||||
|
||||
@field_validator("creation_date", mode="before")
|
||||
@classmethod
|
||||
def parse_creation_date(cls, value: None | datetime | str) -> datetime | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("creation_date")
|
||||
def serialize_creation_date(self, value: datetime | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@field_validator("last_edit_date", mode="before")
|
||||
@classmethod
|
||||
def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None:
|
||||
if value is None:
|
||||
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 | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
@@ -1,21 +1,15 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
|
||||
class DaybookItem(BaseModel):
|
||||
id_: uuid.UUID | None = None
|
||||
date_: date | str
|
||||
date_: date
|
||||
url: list[str]
|
||||
type_: str
|
||||
narration: str
|
||||
@@ -26,42 +20,9 @@ class DaybookItem(BaseModel):
|
||||
posted: bool
|
||||
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")
|
||||
|
||||
|
||||
class Daybook(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
body: list[DaybookItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -1,14 +1,11 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
model_validator,
|
||||
)
|
||||
|
||||
@@ -25,30 +22,6 @@ class EmployeeIn(AccountBase):
|
||||
leaving_date: date | None = None
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("joining_date", mode="before")
|
||||
@classmethod
|
||||
def parse_joining_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("joining_date")
|
||||
def serialize_joining_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("leaving_date", mode="before")
|
||||
@classmethod
|
||||
def parse_leaving_date(cls, value: None | date | str) -> date | None:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
if value is None:
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("leaving_date")
|
||||
def serialize_leaving_date(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def leaving_date_more_than_joining_date(self) -> EmployeeIn:
|
||||
if self.is_active:
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import to_camel
|
||||
from .account import AccountLink
|
||||
@@ -21,17 +15,6 @@ class EmployeeAttendanceItem(BaseModel):
|
||||
full_day: bool | None = None
|
||||
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")
|
||||
|
||||
|
||||
class EmployeeAttendance(BaseModel):
|
||||
start_date: date | None = None
|
||||
@@ -39,29 +22,3 @@ class EmployeeAttendance(BaseModel):
|
||||
employee: AccountLink | None = None
|
||||
body: list[EmployeeAttendanceItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
@@ -2,13 +2,7 @@ import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .user_link import UserLink
|
||||
@@ -29,39 +23,6 @@ class Entries(BaseModel):
|
||||
user: UserLink
|
||||
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")
|
||||
|
||||
@field_validator("creation_date", mode="before")
|
||||
@classmethod
|
||||
def parse_creation_date(cls, value: datetime | str) -> datetime:
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("creation_date")
|
||||
def serialize_creation_date(self, value: datetime, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@field_validator("last_edit_date", mode="before")
|
||||
@classmethod
|
||||
def parse_last_edit_date(cls, value: datetime | str) -> datetime:
|
||||
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:
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
|
||||
class Report(BaseModel):
|
||||
counts: int
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import to_camel
|
||||
|
||||
@@ -18,14 +12,3 @@ class Fingerprint(BaseModel):
|
||||
employee_id: uuid.UUID
|
||||
date_: datetime
|
||||
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("date_", mode="before")
|
||||
@classmethod
|
||||
def parse_date(cls, value: datetime | str) -> datetime:
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("date_")
|
||||
def serialize_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
@@ -1,10 +1,10 @@
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from typing import Self
|
||||
|
||||
from fastapi import Form
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .account import AccountLink
|
||||
@@ -20,17 +20,10 @@ class JournalIn(VoucherIn):
|
||||
journals: list[Journal]
|
||||
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()
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)) -> BaseModel:
|
||||
def load_form(cls, data: str = Form(...)) -> Self:
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
return cls.model_validate(json_data)
|
||||
|
||||
|
||||
class PurchaseIn(VoucherIn):
|
||||
@@ -38,17 +31,10 @@ class PurchaseIn(VoucherIn):
|
||||
inventories: list[Inventory]
|
||||
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()
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)) -> BaseModel:
|
||||
def load_form(cls, data: str = Form(...)) -> Self:
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
return cls.model_validate(json_data)
|
||||
|
||||
|
||||
class IssueIn(VoucherIn):
|
||||
@@ -58,49 +44,35 @@ class IssueIn(VoucherIn):
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def source_destination_unique(self) -> IssueIn:
|
||||
def source_destination_unique(self) -> Self:
|
||||
if self.source.id_ == self.destination.id_:
|
||||
raise ValueError("Source and destination cannot be the same")
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)) -> BaseModel:
|
||||
def load_form(cls, data: str = Form(...)) -> Self:
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
return cls.model_validate(json_data)
|
||||
|
||||
|
||||
class EmployeeBenefitIn(VoucherIn):
|
||||
employee_benefits: list[EmployeeBenefit]
|
||||
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()
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)) -> BaseModel:
|
||||
def load_form(cls, data: str = Form(...)) -> Self:
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
return cls.model_validate(json_data)
|
||||
|
||||
|
||||
class IncentiveIn(VoucherIn):
|
||||
incentives: list[Incentive]
|
||||
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()
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)) -> BaseModel:
|
||||
def load_form(cls, data: str = Form(...)) -> Self:
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
return cls.model_validate(json_data)
|
||||
|
||||
|
||||
class IncentiveEmployee(BaseModel):
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .account import AccountLink
|
||||
@@ -16,7 +10,7 @@ from .account import AccountLink
|
||||
|
||||
class LedgerItem(BaseModel):
|
||||
id_: uuid.UUID | None = None
|
||||
date_: date | str
|
||||
date_: date
|
||||
name: str
|
||||
url: list[str]
|
||||
type_: str
|
||||
@@ -27,43 +21,10 @@ class LedgerItem(BaseModel):
|
||||
tags: list[str]
|
||||
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")
|
||||
|
||||
|
||||
class Ledger(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
account: AccountLink | None = None
|
||||
body: list[LedgerItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -2,13 +2,7 @@ import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .product import ProductLink
|
||||
@@ -26,32 +20,8 @@ class MozimoDailyRegisterItem(BaseModel):
|
||||
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 | str
|
||||
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")
|
||||
@@ -2,13 +2,7 @@ import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .product import ProductLink
|
||||
@@ -26,30 +20,6 @@ class MozimoProductRegisterItem(BaseModel):
|
||||
last_edit_date: datetime | None = None
|
||||
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")
|
||||
|
||||
@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 MozimoProductRegister(BaseModel):
|
||||
start_date: date
|
||||
@@ -57,25 +27,3 @@ class MozimoProductRegister(BaseModel):
|
||||
product: ProductLink | None = None
|
||||
body: list[MozimoProductRegisterItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -20,29 +14,7 @@ class NetTransactionsItem(BaseModel):
|
||||
|
||||
|
||||
class NetTransactions(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
body: list[NetTransactionsItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -19,14 +13,3 @@ class NonContractPurchase(BaseModel):
|
||||
contract_price: Daf
|
||||
purchase_price: Daf
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("date_", mode="before")
|
||||
@classmethod
|
||||
def parse_start_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")
|
||||
@@ -1,15 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from . import to_camel
|
||||
|
||||
@@ -19,28 +12,6 @@ class PeriodIn(BaseModel):
|
||||
valid_till: date
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("valid_from", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_from(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_from")
|
||||
def serialize_valid_from(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("valid_till", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_till(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_till")
|
||||
def serialize_valid_till(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
|
||||
class Period(PeriodIn):
|
||||
id_: uuid.UUID
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .product import ProductLink
|
||||
@@ -32,43 +26,10 @@ class ProductLedgerItem(BaseModel):
|
||||
posted: bool
|
||||
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")
|
||||
|
||||
|
||||
class ProductLedger(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
product: ProductLink | None = None
|
||||
body: list[ProductLedgerItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -26,25 +20,3 @@ class ProfitLoss(BaseModel):
|
||||
body: list[ProfitLossItem]
|
||||
footer: ProfitLossItem
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -21,41 +15,11 @@ class PurchaseEntriesItem(BaseModel):
|
||||
tax: Daf
|
||||
discount: Daf
|
||||
amount: Daf
|
||||
|
||||
@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()
|
||||
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
|
||||
class PurchaseEntries(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
body: list[PurchaseEntriesItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -21,30 +15,8 @@ class PurchasesItem(BaseModel):
|
||||
|
||||
|
||||
class Purchases(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
body: list[PurchasesItem]
|
||||
footer: PurchasesItem
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -2,13 +2,7 @@ import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import to_camel
|
||||
from .account import AccountLink
|
||||
@@ -18,61 +12,17 @@ from .user_link import UserLink
|
||||
|
||||
class RateContractBlank(BaseModel):
|
||||
date_: date
|
||||
valid_from: date | None
|
||||
valid_till: date | None
|
||||
valid_from: date | None = None
|
||||
valid_till: date | None = None
|
||||
narration: str
|
||||
items: list[RateContractItem]
|
||||
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")
|
||||
|
||||
@field_validator("valid_from", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_from(cls, value: date | str | None) -> date | None:
|
||||
if value is None:
|
||||
return value
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_from")
|
||||
def serialize_valid_from(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("valid_till", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_till(cls, value: date | str | None) -> date | None:
|
||||
if value is None:
|
||||
return value
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_till")
|
||||
def serialize_valid_till(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
|
||||
class RateContractIn(RateContractBlank):
|
||||
vendor: AccountLink
|
||||
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()
|
||||
|
||||
|
||||
class RateContract(RateContractIn):
|
||||
id_: uuid.UUID
|
||||
@@ -80,25 +30,3 @@ class RateContract(RateContractIn):
|
||||
last_edit_date: datetime
|
||||
user: UserLink
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("creation_date", mode="before")
|
||||
@classmethod
|
||||
def parse_creation_date(cls, value: datetime | str) -> datetime | None:
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("creation_date")
|
||||
def serialize_creation_date(self, value: datetime, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@field_validator("last_edit_date", mode="before")
|
||||
@classmethod
|
||||
def parse_last_edit_date(cls, value: datetime | str) -> datetime | 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:
|
||||
return value.strftime("%d-%b-%Y %H:%M")
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -32,30 +26,8 @@ class RawMaterialCostItem(BaseModel):
|
||||
|
||||
class RawMaterialCost(BaseModel):
|
||||
id_: uuid.UUID | None = None
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
body: list[RawMaterialCostItem]
|
||||
footer: RawMaterialCostItem | None = None
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -1,6 +1,8 @@
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from typing import Self
|
||||
|
||||
from fastapi import Form
|
||||
from pydantic import (
|
||||
@@ -8,9 +10,6 @@ from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
|
||||
from . import Daf, to_camel
|
||||
@@ -44,23 +43,9 @@ class RecipeIn(BaseModel):
|
||||
|
||||
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")
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)) -> BaseModel:
|
||||
import json
|
||||
|
||||
def load_form(cls, data: str = Form(...)) -> Self:
|
||||
json_data = json.loads(data)
|
||||
# pydantic v2
|
||||
return cls.model_validate(json_data)
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .account import AccountLink
|
||||
@@ -28,56 +22,10 @@ class ReconcileItem(BaseModel):
|
||||
reconcile_date: date | None = None
|
||||
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")
|
||||
|
||||
@field_validator("reconcile_date", mode="before")
|
||||
@classmethod
|
||||
def parse_reconcile_date(cls, value: None | date | str) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("reconcile_date")
|
||||
def serialize_reconcile_date(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
|
||||
class Reconcile(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
account: AccountLink | None = None
|
||||
body: list[ReconcileItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -0,0 +1,18 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import to_camel
|
||||
from .voucher import Voucher
|
||||
|
||||
|
||||
class SaleDayResponse(BaseModel):
|
||||
date_: date
|
||||
selected_voucher_id: uuid.UUID | None = None
|
||||
total_amount: Decimal
|
||||
vouchers: list[Voucher]
|
||||
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import to_camel
|
||||
|
||||
@@ -18,19 +12,6 @@ class LockDate(BaseModel):
|
||||
date_: date | None = None
|
||||
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("date_", mode="before")
|
||||
@classmethod
|
||||
def parse_date(cls, value: None | date | str) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("date_")
|
||||
def serialize_date(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
|
||||
class VoucherTypesSelected(BaseModel):
|
||||
id_: int
|
||||
@@ -59,32 +40,6 @@ class LockInformation(BaseModel):
|
||||
index: int
|
||||
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("valid_from", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_from(cls, value: date | str | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_from")
|
||||
def serialize_valid_from(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("valid_till", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_till(cls, value: date | str | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_till")
|
||||
def serialize_valid_till(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
|
||||
class Maintenance(BaseModel):
|
||||
enabled: bool
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -26,29 +20,7 @@ class StockMovementItem(BaseModel):
|
||||
|
||||
|
||||
class StockMovement(BaseModel):
|
||||
start_date: date | str
|
||||
finish_date: date | str
|
||||
start_date: date
|
||||
finish_date: date
|
||||
body: list[StockMovementItem]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("start_date", mode="before")
|
||||
@classmethod
|
||||
def parse_start_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("start_date")
|
||||
def serialize_start_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("finish_date", mode="before")
|
||||
@classmethod
|
||||
def parse_finish_date(cls, value: date | str) -> date:
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("finish_date")
|
||||
def serialize_finish_date(self, value: date, info: FieldSerializationInfo) -> str:
|
||||
return value.strftime("%d-%b-%Y")
|
||||
@@ -1,10 +1,10 @@
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from typing import Annotated
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, SerializationInfo, field_serializer, field_validator
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .product_group import ProductGroupLink
|
||||
@@ -43,32 +43,6 @@ class Product(BaseModel):
|
||||
valid_till: date | None = None
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("valid_from", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_from(cls, value: str | date | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_from")
|
||||
def serialize_valid_from(self, value: date | None, _info: SerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("valid_till", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_till(cls, value: str | date | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_till")
|
||||
def serialize_valid_till(self, value: date | None, _info: SerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
|
||||
class StockKeepingUnit(BaseModel):
|
||||
id_: uuid.UUID
|
||||
@@ -83,32 +57,6 @@ class StockKeepingUnit(BaseModel):
|
||||
valid_till: date | None = None
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("valid_from", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_from(cls, value: str | date | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_from")
|
||||
def serialize_valid_from(self, value: date | None, _info: SerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("valid_till", mode="before")
|
||||
@classmethod
|
||||
def parse_valid_till(cls, value: str | date | None) -> date | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("valid_till")
|
||||
def serialize_valid_till(self, value: date | None, _info: SerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
|
||||
class TemporalProduct(BaseModel):
|
||||
products: list[Product]
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
|
||||
@@ -20,17 +14,6 @@ class TrialBalanceItem(BaseModel):
|
||||
|
||||
|
||||
class TrialBalance(BaseModel):
|
||||
date_: date | str
|
||||
date_: date
|
||||
body: list[TrialBalanceItem]
|
||||
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")
|
||||
@@ -2,14 +2,7 @@ import uuid
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from . import to_camel
|
||||
from .role import RoleItem
|
||||
@@ -37,19 +30,6 @@ class UserList(BaseModel):
|
||||
last_date: datetime | None = None
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("last_date", mode="before")
|
||||
@classmethod
|
||||
def parse_last_edit_date(cls, value: datetime | str | None) -> datetime | None:
|
||||
if value is None:
|
||||
return value
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y %H:%M")
|
||||
|
||||
@field_serializer("last_date")
|
||||
def serialize_last_edit_date(self, value: datetime, info: FieldSerializationInfo) -> str:
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
|
||||
class UserToken(BaseModel):
|
||||
id_: uuid.UUID
|
||||
|
||||
@@ -2,15 +2,10 @@ import json
|
||||
import uuid
|
||||
|
||||
from datetime import date, datetime
|
||||
from typing import Self
|
||||
|
||||
from fastapi import Form
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
FieldSerializationInfo,
|
||||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from . import Daf, to_camel
|
||||
from .account import AccountLink
|
||||
@@ -34,22 +29,12 @@ class VoucherIn(BaseModel):
|
||||
type_: str
|
||||
files: list[ImageUpload]
|
||||
tags: list[Tag]
|
||||
|
||||
@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")
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@classmethod
|
||||
def load_form(cls, data: str = Form(...)) -> BaseModel:
|
||||
def load_form(cls, data: str = Form(...)) -> Self:
|
||||
json_data = json.loads(data)
|
||||
return cls.parse_obj(json_data)
|
||||
return cls.model_validate(json_data)
|
||||
|
||||
|
||||
class Voucher(VoucherIn):
|
||||
@@ -71,42 +56,3 @@ class Voucher(VoucherIn):
|
||||
employee_benefits: list[EmployeeBenefit]
|
||||
files: list[ImageUpload]
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
@field_validator("reconcile_date", mode="before")
|
||||
@classmethod
|
||||
def parse_reconcile_date(cls, value: None | date | str) -> date | None:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
return datetime.strptime(value, "%d-%b-%Y").date()
|
||||
|
||||
@field_serializer("reconcile_date")
|
||||
def serialize_reconcile_date(self, value: date | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y")
|
||||
|
||||
@field_validator("creation_date", mode="before")
|
||||
@classmethod
|
||||
def parse_creation_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("creation_date")
|
||||
def serialize_creation_date(self, value: datetime | None, info: FieldSerializationInfo) -> str | None:
|
||||
return None if value is None else value.strftime("%d-%b-%Y %H:%M")
|
||||
|
||||
@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")
|
||||
@@ -14,21 +14,19 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^22.1.2",
|
||||
"@angular/cdk": "^22.1.2",
|
||||
"@angular/common": "^22.1.2",
|
||||
"@angular/compiler": "^22.1.2",
|
||||
"@angular/core": "^22.1.2",
|
||||
"@angular/forms": "^22.1.2",
|
||||
"@angular/material": "^22.1.2",
|
||||
"@angular/material-moment-adapter": "^22.1.2",
|
||||
"@angular/material-date-fns-adapter": "^22.1.2",
|
||||
"@angular/platform-browser": "^22.1.2",
|
||||
"@angular/platform-browser-dynamic": "^22.1.2",
|
||||
"@angular/router": "^22.1.2",
|
||||
"date-fns": "^4.4.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^4.2.0",
|
||||
"mathjs": "^15.1.1",
|
||||
"moment": "^2.30.1",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.6.0"
|
||||
},
|
||||
|
||||
@@ -6,11 +6,12 @@ import {
|
||||
LOCALE_ID,
|
||||
provideZonelessChangeDetection,
|
||||
} from '@angular/core';
|
||||
import { MomentDateAdapter } from '@angular/material-moment-adapter';
|
||||
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
|
||||
import { provideDateFnsAdapter } from '@angular/material-date-fns-adapter';
|
||||
import { MAT_DATE_LOCALE } from '@angular/material/core';
|
||||
import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { provideRouter, withComponentInputBinding } from '@angular/router';
|
||||
import { enIN } from 'date-fns/locale';
|
||||
|
||||
import { dateFormat } from './app.environment';
|
||||
import { routes } from './app.routes';
|
||||
@@ -24,6 +25,7 @@ export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
importProvidersFrom(BrowserModule),
|
||||
{ provide: LOCALE_ID, useValue: 'en-IN' },
|
||||
{ provide: MAT_DATE_LOCALE, useValue: enIN },
|
||||
AuthService,
|
||||
provideHttpClient(
|
||||
withInterceptors([
|
||||
@@ -34,8 +36,7 @@ export const appConfig: ApplicationConfig = {
|
||||
]),
|
||||
),
|
||||
provideRouter(routes, withComponentInputBinding()),
|
||||
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
|
||||
{ provide: MAT_DATE_FORMATS, useValue: dateFormat },
|
||||
provideDateFnsAdapter(dateFormat),
|
||||
{ provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 3000 } },
|
||||
provideZonelessChangeDetection(),
|
||||
],
|
||||
|
||||
@@ -6,12 +6,12 @@ export const environment = {
|
||||
|
||||
export const dateFormat = {
|
||||
parse: {
|
||||
dateInput: 'DD-MMM-YYYY',
|
||||
dateInput: ['dd-MMM-yyyy', 'dd/MM/yyyy', 'dd-MM-yyyy', 'yyyy-MM-dd'],
|
||||
},
|
||||
display: {
|
||||
dateInput: 'DD-MMM-YYYY',
|
||||
monthYearLabel: 'MMM YYYY',
|
||||
dateA11yLabel: 'DD-MMM-YYYY',
|
||||
monthYearA11yLabel: 'MMM YYYY',
|
||||
dateInput: 'dd-MMM-yyyy',
|
||||
monthYearLabel: 'MMM yyyy',
|
||||
dateA11yLabel: 'dd-MMM-yyyy',
|
||||
monthYearA11yLabel: 'MMMM yyyy',
|
||||
},
|
||||
};
|
||||
@@ -153,6 +153,10 @@ export const routes: Routes = [
|
||||
path: 'sales-import',
|
||||
loadChildren: () => import('./sales-import/sales-import.routes').then((mod) => mod.routes),
|
||||
},
|
||||
{
|
||||
path: 'sale',
|
||||
loadChildren: () => import('./sale/sale.routes').then((mod) => mod.routes),
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
loadChildren: () => import('./settings/settings.routes').then((mod) => mod.routes),
|
||||
|
||||
@@ -12,9 +12,9 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { Attendance } from './attendance';
|
||||
@@ -66,7 +66,7 @@ export class AttendanceComponent {
|
||||
model = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (infoVal): AttendanceFormData => ({
|
||||
date: moment(infoVal.date, 'DD-MMM-YYYY').toDate(),
|
||||
date: parseDate(infoVal.date),
|
||||
attendances: infoVal.body.map((x) => ({
|
||||
attendanceType: x.attendanceType.id,
|
||||
})),
|
||||
@@ -98,7 +98,7 @@ export class AttendanceComponent {
|
||||
}
|
||||
|
||||
show() {
|
||||
const date = moment(this.model().date).format('DD-MMM-YYYY');
|
||||
const date = formatIsoDate(this.model().date);
|
||||
if (this.date() === date) {
|
||||
this.infoResource.reload();
|
||||
} else {
|
||||
@@ -120,7 +120,7 @@ export class AttendanceComponent {
|
||||
getAttendance(): Attendance {
|
||||
const formModel = this.model();
|
||||
const currentInfo = this.info();
|
||||
currentInfo.date = moment(formModel.date).format('DD-MMM-YYYY');
|
||||
currentInfo.date = formatIsoDate(formModel.date);
|
||||
const array = formModel.attendances;
|
||||
if (array) {
|
||||
currentInfo.body.forEach((item, index) => {
|
||||
|
||||
@@ -12,8 +12,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ClearPipe } from '../shared/clear.pipe';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
@@ -58,7 +58,7 @@ export class BalanceSheetComponent {
|
||||
formModel = linkedSignal<import('./balance-sheet').BalanceSheet, BalanceSheetFormData>({
|
||||
source: this.info,
|
||||
computation: (info: BalanceSheet): BalanceSheetFormData => ({
|
||||
date: info.date ? moment(info.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(info.date),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -121,7 +121,7 @@ export class BalanceSheetComponent {
|
||||
|
||||
getDate(): string {
|
||||
const formModel = this.formModel();
|
||||
return moment(formModel.date).format('DD-MMM-YYYY');
|
||||
return formatIsoDate(formModel.date);
|
||||
}
|
||||
|
||||
handlePageEvent(e: PageEvent) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-2-5">
|
||||
<mat-label>Start Date</mat-label>
|
||||
<input matInput [matDatepicker]="startDatePicker" [formField]="form.startDate" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="startDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #startDatePicker></mat-datepicker>
|
||||
<input matInput [matDatepicker]="startDate" [formField]="form.startDate" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle>
|
||||
<mat-datepicker #startDate></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-2-5">
|
||||
<mat-label>Finish Date</mat-label>
|
||||
|
||||
@@ -12,8 +12,8 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ClearPipe } from '../shared/clear.pipe';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
@@ -78,11 +78,11 @@ export class CashFlowComponent {
|
||||
|
||||
dataSource = computed(() => this.sortedList());
|
||||
|
||||
model = linkedSignal({
|
||||
model = linkedSignal<import('./cash-flow').CashFlow, CashFlowFormData>({
|
||||
source: this.info,
|
||||
computation: (info: CashFlow): CashFlowFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -121,8 +121,8 @@ export class CashFlowComponent {
|
||||
const formModel = this.model();
|
||||
|
||||
return new CashFlow({
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { CostCentre } from '../core/cost-centre';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { User } from '../core/user';
|
||||
import { CostCentreService } from '../cost-centre/cost-centre.service';
|
||||
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
|
||||
@@ -85,7 +85,7 @@ export class ClosingStockComponent {
|
||||
model = linkedSignal({
|
||||
source: computed(() => ({ info: this.info(), costCentres: this.costCentres() })),
|
||||
computation: ({ info, costCentres }): ClosingStockFormData => ({
|
||||
date: moment(info.date, 'DD-MMM-YYYY').toDate(),
|
||||
date: parseDate(info.date),
|
||||
costCentre: costCentres.find((c) => c.id === info.costCentre?.id) || null,
|
||||
stocks: info.items.map((x) => ({
|
||||
physical: x.physical,
|
||||
@@ -176,7 +176,7 @@ export class ClosingStockComponent {
|
||||
getClosingStock(): ClosingStock {
|
||||
const formModel = this.model();
|
||||
const currentInfo = this.info();
|
||||
currentInfo.date = moment(formModel.date).format('DD-MMM-YYYY');
|
||||
currentInfo.date = formatIsoDate(formModel.date);
|
||||
const array = formModel.stocks;
|
||||
currentInfo.items.forEach((item, index) => {
|
||||
item.physical = array[index].physical ?? 0;
|
||||
@@ -189,7 +189,7 @@ export class ClosingStockComponent {
|
||||
const formModel = this.model();
|
||||
|
||||
return new ClosingStock({
|
||||
date: moment(formModel.date).format('DD-MMM-YYYY'),
|
||||
date: formatIsoDate(formModel.date),
|
||||
costCentre: formModel.costCentre ?? new CostCentre(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { endOfMonth, format, getDaysInMonth, isValid, parse, parseISO, startOfMonth } from 'date-fns';
|
||||
|
||||
export const ISO_DATE_FORMAT = 'yyyy-MM-dd';
|
||||
export const DISPLAY_DATE_FORMAT = 'dd-MMM-yyyy';
|
||||
export const DISPLAY_DATETIME_FORMAT = 'dd-MMM-yyyy HH:mm';
|
||||
|
||||
export function isValidDate(value: unknown): value is Date {
|
||||
return value instanceof Date && isValid(value);
|
||||
}
|
||||
|
||||
export function formatIsoDate(date: Date | null | undefined): string {
|
||||
if (!date || !isValid(date)) return '';
|
||||
return format(date, ISO_DATE_FORMAT);
|
||||
}
|
||||
|
||||
export function formatDisplayDate(date: Date | null | undefined): string {
|
||||
if (!date || !isValid(date)) return '';
|
||||
return format(date, DISPLAY_DATE_FORMAT);
|
||||
}
|
||||
|
||||
export function formatDisplayDateTime(date: Date | null | undefined): string {
|
||||
if (!date || !isValid(date)) return '';
|
||||
return format(date, DISPLAY_DATETIME_FORMAT);
|
||||
}
|
||||
|
||||
export function parseDate(value: string | Date | null | undefined): Date;
|
||||
export function parseDate(value: string | Date | null | undefined, defaultValue: null): Date | null;
|
||||
export function parseDate(value: string | Date | null | undefined, defaultValue: Date): Date;
|
||||
export function parseDate(
|
||||
value: string | Date | null | undefined,
|
||||
defaultValue: Date | null = new Date(),
|
||||
): Date | null {
|
||||
if (!value) return defaultValue;
|
||||
if (value instanceof Date) return value;
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return defaultValue;
|
||||
|
||||
// ISO 8601 strings (YYYY-MM-DD or full ISO datetime)
|
||||
if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) {
|
||||
return parseISO(trimmed);
|
||||
}
|
||||
|
||||
// Common UI format: dd-MMM-yyyy (e.g. 15-Sep-2026)
|
||||
const parsedCustom = parse(trimmed, 'dd-MMM-yyyy', new Date());
|
||||
if (isValid(parsedCustom)) {
|
||||
return parsedCustom;
|
||||
}
|
||||
|
||||
// Fallback to native date parsing
|
||||
const fallback = new Date(trimmed);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export { endOfMonth, getDaysInMonth, startOfMonth };
|
||||
@@ -13,8 +13,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { Daybook } from './daybook';
|
||||
@@ -98,8 +98,8 @@ export class DaybookComponent {
|
||||
model = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (info: Daybook): DaybookFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -143,8 +143,8 @@ export class DaybookComponent {
|
||||
const formModel = this.model();
|
||||
|
||||
return {
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
body: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AttendanceTypeService } from '../attendance/attendance-type.service';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { Employee } from '../employee/employee';
|
||||
import { EmployeeService } from '../employee/employee.service';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
@@ -69,9 +69,9 @@ export class EmployeeAttendanceComponent {
|
||||
|
||||
model = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (info): EmployeeAttendanceFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
computation: (info: EmployeeAttendance): EmployeeAttendanceFormData => ({
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
employee: info.employee ?? null,
|
||||
attendances: info.body.map((x) => ({
|
||||
attendanceType: x.attendanceType.id,
|
||||
@@ -121,8 +121,8 @@ export class EmployeeAttendanceComponent {
|
||||
show() {
|
||||
const formValue = this.model();
|
||||
const currentInfo = this.info();
|
||||
currentInfo.startDate = moment(formValue.startDate).format('DD-MMM-YYYY');
|
||||
currentInfo.finishDate = moment(formValue.finishDate).format('DD-MMM-YYYY');
|
||||
currentInfo.startDate = formatIsoDate(formValue.startDate);
|
||||
currentInfo.finishDate = formatIsoDate(formValue.finishDate);
|
||||
currentInfo.employee =
|
||||
typeof formValue.employee !== 'string' && formValue.employee ? formValue.employee : undefined;
|
||||
if (currentInfo.employee) {
|
||||
@@ -157,8 +157,8 @@ export class EmployeeAttendanceComponent {
|
||||
getEmployeeAttendance(): EmployeeAttendance {
|
||||
const formValue = this.model();
|
||||
const currentInfo = this.info();
|
||||
currentInfo.startDate = moment(formValue.startDate).format('DD-MMM-YYYY');
|
||||
currentInfo.finishDate = moment(formValue.finishDate).format('DD-MMM-YYYY');
|
||||
currentInfo.startDate = formatIsoDate(formValue.startDate);
|
||||
currentInfo.finishDate = formatIsoDate(formValue.finishDate);
|
||||
currentInfo.employee =
|
||||
typeof formValue.employee !== 'string' && formValue.employee ? formValue.employee : undefined;
|
||||
const array = formValue.attendances;
|
||||
|
||||
@@ -12,9 +12,9 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { formatIsoDate, getDaysInMonth, parseDate } from '../core/date-utils';
|
||||
import { EmployeeBenefit } from '../core/employee-benefit';
|
||||
import { User } from '../core/user';
|
||||
import { Voucher } from '../core/voucher';
|
||||
@@ -80,7 +80,7 @@ export class EmployeeBenefitsComponent {
|
||||
formModel = linkedSignal<Voucher, EmployeeBenefitsFormData>({
|
||||
source: this.item,
|
||||
computation: (v): EmployeeBenefitsFormData => ({
|
||||
date: v.date ? moment(v.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(v.date),
|
||||
addRow: {
|
||||
employee: null as Employee | string | null,
|
||||
grossSalary: '',
|
||||
@@ -168,7 +168,7 @@ export class EmployeeBenefitsComponent {
|
||||
}
|
||||
const grossSalary = Number(formValue?.grossSalary ?? 0);
|
||||
const daysWorked = Number(formValue?.daysWorked ?? 0);
|
||||
const daysInMonth = moment(this.formModel().date).daysInMonth();
|
||||
const daysInMonth = getDaysInMonth(this.formModel().date);
|
||||
const esi = EmployeeBenefitsComponent.getEsi(grossSalary, daysWorked, daysInMonth);
|
||||
const pf = EmployeeBenefitsComponent.getPf(grossSalary, daysWorked, daysInMonth);
|
||||
|
||||
@@ -245,7 +245,7 @@ export class EmployeeBenefitsComponent {
|
||||
const m = this.formModel();
|
||||
const base = this.item();
|
||||
const v = new Voucher({ ...base });
|
||||
v.date = moment(m.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(m.date);
|
||||
v.employeeBenefits = m.benefits.map(
|
||||
(b) =>
|
||||
new EmployeeBenefit({
|
||||
|
||||
@@ -6,8 +6,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import moment from 'moment';
|
||||
|
||||
import { endOfMonth, formatIsoDate } from '../core/date-utils';
|
||||
import { EmployeeFunctionsService } from './employee-functions.service';
|
||||
|
||||
@Component({
|
||||
@@ -28,15 +28,15 @@ export class EmployeeFunctionsComponent {
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private ser = inject(EmployeeFunctionsService);
|
||||
|
||||
creditSalaryModel = signal({
|
||||
date: moment().date(moment().daysInMonth()),
|
||||
creditSalaryModel = signal<{ date: Date }>({
|
||||
date: endOfMonth(new Date()),
|
||||
});
|
||||
|
||||
creditSalaryForm = createForm(this.creditSalaryModel);
|
||||
|
||||
attendanceRecordModel = signal({
|
||||
startDate: moment(),
|
||||
finishDate: moment(),
|
||||
attendanceRecordModel = signal<{ startDate: Date; finishDate: Date }>({
|
||||
startDate: new Date(),
|
||||
finishDate: new Date(),
|
||||
});
|
||||
|
||||
attendanceRecordForm = createForm(this.attendanceRecordModel);
|
||||
@@ -44,28 +44,24 @@ export class EmployeeFunctionsComponent {
|
||||
fingerprintFile: File | null = null;
|
||||
|
||||
constructor() {
|
||||
const startDate = moment().date(1);
|
||||
const finishDate = moment().date(startDate.daysInMonth());
|
||||
this.creditSalaryModel.set({ date: finishDate });
|
||||
this.creditSalaryModel.set({ date: endOfMonth(new Date()) });
|
||||
}
|
||||
|
||||
chosenYearHandler(normalizedYear: moment.Moment) {
|
||||
const ctrlValue = moment(this.creditSalaryModel().date);
|
||||
ctrlValue.year(normalizedYear.year());
|
||||
ctrlValue.date(ctrlValue.daysInMonth());
|
||||
this.creditSalaryModel.update((m) => ({ ...m, date: ctrlValue }));
|
||||
chosenYearHandler(normalizedYear: Date) {
|
||||
const current = this.creditSalaryModel().date;
|
||||
const updated = new Date(normalizedYear.getFullYear(), current.getMonth(), 1);
|
||||
this.creditSalaryModel.update((m) => ({ ...m, date: endOfMonth(updated) }));
|
||||
}
|
||||
|
||||
chosenMonthHandler(normlizedMonth: moment.Moment, datepicker: MatDatepicker<moment.Moment>) {
|
||||
const ctrlValue = moment(this.creditSalaryModel().date);
|
||||
ctrlValue.month(normlizedMonth.month());
|
||||
ctrlValue.date(ctrlValue.daysInMonth());
|
||||
this.creditSalaryModel.update((m) => ({ ...m, date: ctrlValue }));
|
||||
chosenMonthHandler(normalizedMonth: Date, datepicker: MatDatepicker<Date>) {
|
||||
const current = this.creditSalaryModel().date;
|
||||
const updated = new Date(current.getFullYear(), normalizedMonth.getMonth(), 1);
|
||||
this.creditSalaryModel.update((m) => ({ ...m, date: endOfMonth(updated) }));
|
||||
datepicker.close();
|
||||
}
|
||||
|
||||
creditSalary() {
|
||||
const date = (this.creditSalaryModel().date ?? moment()).format('DD-MMM-YYYY');
|
||||
const date = formatIsoDate(this.creditSalaryModel().date);
|
||||
if (!date) {
|
||||
this.snackBar.open('Please choose a valid date.', 'Danger');
|
||||
return;
|
||||
@@ -81,20 +77,20 @@ export class EmployeeFunctionsComponent {
|
||||
}
|
||||
|
||||
attendanceRecordUrl() {
|
||||
const startDate = (this.attendanceRecordModel().startDate ?? moment()).format('DD-MMM-YYYY');
|
||||
const finishDate = (this.attendanceRecordModel().finishDate ?? moment()).format('DD-MMM-YYYY');
|
||||
const startDate = formatIsoDate(this.attendanceRecordModel().startDate ?? new Date());
|
||||
const finishDate = formatIsoDate(this.attendanceRecordModel().finishDate ?? new Date());
|
||||
if (!startDate || !finishDate) {
|
||||
// this.snackBar.open('Please choose a start and finish date.', 'Danger');
|
||||
this.snackBar.open('Please choose a start and finish date.', 'Danger');
|
||||
return '';
|
||||
}
|
||||
return `/attendance-report?s=${startDate}&f=${finishDate}`;
|
||||
}
|
||||
|
||||
fingerPrintUrl() {
|
||||
const startDate = (this.attendanceRecordModel().startDate ?? moment()).format('DD-MMM-YYYY');
|
||||
const finishDate = (this.attendanceRecordModel().finishDate ?? moment()).format('DD-MMM-YYYY');
|
||||
const startDate = formatIsoDate(this.attendanceRecordModel().startDate);
|
||||
const finishDate = formatIsoDate(this.attendanceRecordModel().finishDate);
|
||||
if (!startDate || !finishDate) {
|
||||
// this.snackBar.open('Please choose a start and finish date.', 'Danger');
|
||||
this.snackBar.open('Please choose a start and finish date.', 'Danger');
|
||||
return '';
|
||||
}
|
||||
return `/fingerprint-report?s=${startDate}&f=${finishDate}`;
|
||||
|
||||
@@ -11,8 +11,8 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../../core/date-utils';
|
||||
import { CostCentreService } from '../../cost-centre/cost-centre.service';
|
||||
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
|
||||
import { ErrorStateComponent } from '../../shared/error-state/error-state.component';
|
||||
@@ -70,8 +70,8 @@ export class EmployeeDetailComponent {
|
||||
points: itemVal.points || 0,
|
||||
isActive: itemVal.isActive ?? true,
|
||||
costCentre: itemVal.costCentre?.id ?? '',
|
||||
joiningDate: itemVal.joiningDate ? moment(itemVal.joiningDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
leavingDate: itemVal.leavingDate ? moment(itemVal.leavingDate, 'DD-MMM-YYYY').toDate() : null,
|
||||
joiningDate: parseDate(itemVal.joiningDate),
|
||||
leavingDate: parseDate(itemVal.leavingDate, null),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -147,9 +147,8 @@ export class EmployeeDetailComponent {
|
||||
item.points = Number(formValue.points);
|
||||
item.isActive = formValue.isActive;
|
||||
item.costCentre.id = formValue.costCentre ?? '';
|
||||
item.joiningDate = moment(formValue.joiningDate).format('DD-MMM-YYYY');
|
||||
item.leavingDate =
|
||||
item.isActive || !formValue.leavingDate ? null : moment(formValue.leavingDate).format('DD-MMM-YYYY');
|
||||
item.joiningDate = formatIsoDate(formValue.joiningDate);
|
||||
item.leavingDate = item.isActive || !formValue.leavingDate ? null : formatIsoDate(formValue.leavingDate);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,9 @@
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-2-5">
|
||||
<mat-label>Create / Last Edit Date From</mat-label>
|
||||
<input
|
||||
matInput
|
||||
#startDateElement
|
||||
[matDatepicker]="startDatePicker"
|
||||
[formField]="form.startDate"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="startDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #startDatePicker></mat-datepicker>
|
||||
<input matInput [matDatepicker]="startDate" [formField]="form.startDate" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle>
|
||||
<mat-datepicker #startDate></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-2-5">
|
||||
<mat-label>Create / Last Edit Date Till</mat-label>
|
||||
|
||||
@@ -19,8 +19,8 @@ import { MatRadioChange, MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { LocalTimePipe } from '../shared/local-time.pipe';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
@@ -103,8 +103,8 @@ export class EntriesComponent {
|
||||
issue: this.issue(),
|
||||
}),
|
||||
computation: (src): EntriesFormData => ({
|
||||
startDate: src.startDate ? moment(src.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: src.finishDate ? moment(src.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(src.startDate),
|
||||
finishDate: parseDate(src.finishDate),
|
||||
posted: src.posted,
|
||||
issue: src.issue,
|
||||
}),
|
||||
@@ -127,8 +127,8 @@ export class EntriesComponent {
|
||||
|
||||
refresh() {
|
||||
const formModel = this.formModel();
|
||||
const startDate = moment(formModel.startDate).format('DD-MMM-YYYY');
|
||||
const finishDate = moment(formModel.finishDate).format('DD-MMM-YYYY');
|
||||
const startDate = formatIsoDate(formModel.startDate);
|
||||
const finishDate = formatIsoDate(formModel.finishDate);
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
queryParams: { s: startDate, f: finishDate },
|
||||
|
||||
@@ -10,11 +10,11 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { Account } from '../core/account';
|
||||
import { AccountBalance } from '../core/account-balance';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { Incentive } from '../core/incentive';
|
||||
import { User } from '../core/user';
|
||||
import { Voucher } from '../core/voucher';
|
||||
@@ -23,7 +23,7 @@ import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.
|
||||
import { LocalTimePipe } from '../shared/local-time.pipe';
|
||||
|
||||
export interface IncentiveFormData {
|
||||
date: moment.Moment;
|
||||
date: Date;
|
||||
incentives: Incentive[];
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export class IncentiveComponent {
|
||||
model = linkedSignal({
|
||||
source: this.voucher,
|
||||
computation: (v): IncentiveFormData => ({
|
||||
date: v.date ? moment(v.date, 'DD-MMM-YYYY') : moment(new Date()),
|
||||
date: parseDate(v.date),
|
||||
incentives: v.incentives.map((x) => new Incentive({ ...x })),
|
||||
}),
|
||||
});
|
||||
@@ -71,7 +71,7 @@ export class IncentiveComponent {
|
||||
|
||||
constructor() {
|
||||
effect(async () => {
|
||||
const date = this.form.date().value().format('DD-MMM-YYYY');
|
||||
const date = formatIsoDate(this.form.date().value());
|
||||
if (date !== this.d() && !this.id()) {
|
||||
this.router.navigate(['/incentive'], { queryParams: { d: date }, replaceUrl: true });
|
||||
}
|
||||
@@ -167,7 +167,7 @@ export class IncentiveComponent {
|
||||
getVoucher(): Voucher {
|
||||
const formModel = this.model();
|
||||
const v = this.voucher();
|
||||
v.date = moment(formModel.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(formModel.date);
|
||||
v.incentives = formModel.incentives;
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { round } from 'mathjs';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { Batch } from '../core/batch';
|
||||
import { BatchService } from '../core/batch.service';
|
||||
import { CostCentre } from '../core/cost-centre';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { Inventory } from '../core/inventory';
|
||||
import { User } from '../core/user';
|
||||
import { Voucher } from '../core/voucher';
|
||||
@@ -118,7 +118,7 @@ export class IssueComponent {
|
||||
2,
|
||||
);
|
||||
return {
|
||||
date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(voucher.date),
|
||||
source: (voucher.source as CostCentre)?.id ?? '',
|
||||
destination: (voucher.destination as CostCentre)?.id ?? '',
|
||||
amount,
|
||||
@@ -150,7 +150,7 @@ export class IssueComponent {
|
||||
displayedColumns = ['product', 'batch', 'quantity', 'rate', 'amount', 'action'];
|
||||
gridColumns = ['source', 'destination', 'gridAmount', 'load'];
|
||||
|
||||
balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY'));
|
||||
balanceDate = computed(() => formatIsoDate(this.form.date().value()));
|
||||
|
||||
batchSearch = computed(() => {
|
||||
const v = this.form.addRow.batch().value();
|
||||
@@ -200,7 +200,7 @@ export class IssueComponent {
|
||||
if (!d) return;
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('d', moment(d).format('DD-MMM-YYYY'));
|
||||
url.searchParams.set('d', formatIsoDate(d));
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
});
|
||||
}
|
||||
@@ -291,7 +291,7 @@ export class IssueComponent {
|
||||
amount: row.amount,
|
||||
batch: row.batch,
|
||||
}),
|
||||
date: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
date: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -337,7 +337,7 @@ export class IssueComponent {
|
||||
const m = this.formModel();
|
||||
const base = this.item();
|
||||
const v = new Voucher({ ...base });
|
||||
v.date = moment(m.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(m.date);
|
||||
v.source = new CostCentre({ id: m.source });
|
||||
v.destination = new CostCentre({ id: m.destination });
|
||||
v.narration = m.narration ?? '';
|
||||
|
||||
@@ -17,13 +17,13 @@ import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { round } from 'mathjs';
|
||||
import moment from 'moment';
|
||||
import { zip } from 'rxjs';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { Account } from '../core/account';
|
||||
import { AccountService } from '../core/account.service';
|
||||
import { CostCentre } from '../core/cost-centre';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { DbFile } from '../core/db-file';
|
||||
import { Journal } from '../core/journal';
|
||||
import { User } from '../core/user';
|
||||
@@ -135,7 +135,7 @@ export class JournalComponent {
|
||||
event.preventDefault();
|
||||
this.router.navigate(['payment'], {
|
||||
queryParams: {
|
||||
d: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
d: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@@ -145,7 +145,7 @@ export class JournalComponent {
|
||||
event.preventDefault();
|
||||
this.router.navigate(['receipt'], {
|
||||
queryParams: {
|
||||
d: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
d: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@@ -175,7 +175,7 @@ export class JournalComponent {
|
||||
source: this.item,
|
||||
computation: (voucher): JournalFormModel => ({
|
||||
isStarred: voucher.isStarred,
|
||||
date: moment(voucher.date, 'DD-MMM-YYYY').toDate(),
|
||||
date: parseDate(voucher.date),
|
||||
addRow: {
|
||||
debit: 1,
|
||||
account: null as Account | string | null,
|
||||
@@ -218,7 +218,7 @@ export class JournalComponent {
|
||||
tagsResource = this.tagSer.autocomplete(this.tagQuery);
|
||||
tags = computed(() => this.tagsResource.value() ?? []);
|
||||
|
||||
balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY'));
|
||||
balanceDate = computed(() => formatIsoDate(this.form.date().value()));
|
||||
accountId = computed((): string | null => {
|
||||
const account = this.form.addRow.account().value();
|
||||
return typeof account === 'string' ? null : (account?.id ?? null);
|
||||
@@ -237,7 +237,7 @@ export class JournalComponent {
|
||||
|
||||
dateChanged(newDate: Date) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { d: moment(newDate).format('DD-MMM-YYYY') },
|
||||
queryParams: { d: formatIsoDate(newDate) },
|
||||
replaceUrl: true,
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@@ -292,7 +292,7 @@ export class JournalComponent {
|
||||
width: '750px',
|
||||
data: {
|
||||
journal: new Journal({ ...row }),
|
||||
date: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
date: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -328,7 +328,7 @@ export class JournalComponent {
|
||||
const m = this.formModel();
|
||||
const base = this.item();
|
||||
const v = new Voucher({ ...base });
|
||||
v.date = moment(m.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(m.date);
|
||||
v.narration = m.narration ?? '';
|
||||
v.journals = m.journals.map(
|
||||
(j) => new Journal({ id: j.id, debit: j.debit, amount: j.amount, account: j.account, costCentre: j.costCentre }),
|
||||
|
||||
@@ -17,10 +17,10 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { Account } from '../core/account';
|
||||
import { AccountService } from '../core/account.service';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { AccountingPipe } from '../shared/accounting.pipe';
|
||||
import { ClearPipe } from '../shared/clear.pipe';
|
||||
import { ToCsvService } from '../shared/to-csv.service';
|
||||
@@ -148,8 +148,8 @@ export class LedgerComponent {
|
||||
model = linkedSignal<import('./ledger').Ledger, LedgerFormData>({
|
||||
source: this.info,
|
||||
computation: (info: Ledger): LedgerFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
account: info.account ?? null,
|
||||
tags: this.tags(),
|
||||
}),
|
||||
@@ -283,8 +283,8 @@ export class LedgerComponent {
|
||||
|
||||
return new Ledger({
|
||||
account,
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { LocalTimePipe } from '../shared/local-time.pipe';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
@@ -78,7 +78,7 @@ export class MozimoDailyRegisterComponent {
|
||||
model = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (v): MozimoDailyRegisterFormData => ({
|
||||
date: moment(v.date, 'DD-MMM-YYYY').toDate(),
|
||||
date: parseDate(v.date),
|
||||
items: v.body.map((x) => ({
|
||||
received: x.received,
|
||||
sale: x.sale,
|
||||
@@ -110,7 +110,7 @@ export class MozimoDailyRegisterComponent {
|
||||
}
|
||||
|
||||
show() {
|
||||
const date = moment(this.model().date).format('DD-MMM-YYYY');
|
||||
const date = formatIsoDate(this.model().date);
|
||||
if (this.date() === date) {
|
||||
this.infoResource.reload();
|
||||
} else {
|
||||
@@ -132,7 +132,7 @@ export class MozimoDailyRegisterComponent {
|
||||
getMozimoDailyRegister(): MozimoDailyRegister {
|
||||
const formModel = this.model();
|
||||
const info = this.info();
|
||||
info.date = moment(formModel.date).format('DD-MMM-YYYY');
|
||||
info.date = formatIsoDate(formModel.date);
|
||||
|
||||
const array = formModel.items;
|
||||
if (array) {
|
||||
|
||||
@@ -13,9 +13,9 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { Product } from '../core/product';
|
||||
import { ProductService } from '../product/product.service';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
@@ -84,8 +84,8 @@ export class MozimoProductRegisterComponent {
|
||||
model = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (info): MozimoProductRegisterFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
product: info.product ?? '',
|
||||
items: info.body.map((x) => ({
|
||||
received: x.received,
|
||||
@@ -167,8 +167,8 @@ export class MozimoProductRegisterComponent {
|
||||
getMozimoProductRegister(): MozimoProductRegister {
|
||||
const formModel = this.model();
|
||||
const info = this.info();
|
||||
info.startDate = moment(formModel.startDate).format('DD-MMM-YYYY');
|
||||
info.finishDate = moment(formModel.finishDate).format('DD-MMM-YYYY');
|
||||
info.startDate = formatIsoDate(formModel.startDate);
|
||||
info.finishDate = formatIsoDate(formModel.finishDate);
|
||||
|
||||
const array = formModel.items;
|
||||
if (array) {
|
||||
@@ -190,8 +190,8 @@ export class MozimoProductRegisterComponent {
|
||||
|
||||
return new MozimoProductRegister({
|
||||
product: formModel.product as Product,
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto basis-2-5">
|
||||
<mat-label>Start Date</mat-label>
|
||||
<input matInput [matDatepicker]="startDatePicker" [formField]="form.startDate" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="startDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #startDatePicker></mat-datepicker>
|
||||
<input matInput [matDatepicker]="startDate" [formField]="form.startDate" autocomplete="off" />
|
||||
<mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle>
|
||||
<mat-datepicker #startDate></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-auto basis-2-5">
|
||||
<mat-label>Finish Date</mat-label>
|
||||
|
||||
@@ -13,8 +13,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { AccountingPipe } from '../shared/accounting.pipe';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
@@ -92,8 +92,8 @@ export class NetTransactionsComponent {
|
||||
model = linkedSignal<import('./net-transactions').NetTransactions, NetTransactionsFormData>({
|
||||
source: this.info,
|
||||
computation: (info: NetTransactions): NetTransactionsFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -133,8 +133,8 @@ export class NetTransactionsComponent {
|
||||
const formModel = this.model();
|
||||
|
||||
return new NetTransactions({
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { round } from 'mathjs';
|
||||
import moment from 'moment';
|
||||
import { zip } from 'rxjs';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { Account } from '../core/account';
|
||||
import { AccountService } from '../core/account.service';
|
||||
import { CostCentre } from '../core/cost-centre';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { DbFile } from '../core/db-file';
|
||||
import { Journal } from '../core/journal';
|
||||
import { User } from '../core/user';
|
||||
@@ -133,7 +133,7 @@ export class PaymentComponent {
|
||||
);
|
||||
return {
|
||||
isStarred: voucher.isStarred,
|
||||
date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(voucher.date),
|
||||
paymentAccount: pj?.account?.id ?? this.a() ?? '',
|
||||
paymentAmount,
|
||||
addRow: {
|
||||
@@ -180,7 +180,7 @@ export class PaymentComponent {
|
||||
tagsResource = this.tagSer.autocomplete(this.tagQuery);
|
||||
tags = computed(() => this.tagsResource.value() ?? []);
|
||||
|
||||
balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY'));
|
||||
balanceDate = computed(() => formatIsoDate(this.form.date().value()));
|
||||
accountId = computed((): string | null => {
|
||||
const account = this.form.addRow.account().value();
|
||||
return typeof account === 'string' ? null : (account?.id ?? null);
|
||||
@@ -206,7 +206,7 @@ export class PaymentComponent {
|
||||
this.router.navigate(['receipt'], {
|
||||
queryParams: {
|
||||
a: this.formModel().paymentAccount,
|
||||
d: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
d: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@@ -216,7 +216,7 @@ export class PaymentComponent {
|
||||
event.preventDefault();
|
||||
this.router.navigate(['journal'], {
|
||||
queryParams: {
|
||||
d: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
d: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@@ -246,7 +246,7 @@ export class PaymentComponent {
|
||||
if (!d) return;
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('d', moment(d).format('DD-MMM-YYYY'));
|
||||
url.searchParams.set('d', formatIsoDate(d));
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
});
|
||||
|
||||
@@ -319,7 +319,7 @@ export class PaymentComponent {
|
||||
account: row.account,
|
||||
costCentre: row.costCentre,
|
||||
}),
|
||||
date: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
date: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -358,7 +358,7 @@ export class PaymentComponent {
|
||||
const m = this.formModel();
|
||||
const base = this.item();
|
||||
const v = new Voucher({ ...base });
|
||||
v.date = moment(m.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(m.date);
|
||||
v.narration = m.narration ?? '';
|
||||
|
||||
const existingPj = base.journals.find((x) => x.debit === -1);
|
||||
|
||||
@@ -11,8 +11,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../../core/date-utils';
|
||||
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
|
||||
import { ErrorStateComponent } from '../../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../../shared/skeleton-loader/skeleton-loader.component';
|
||||
@@ -48,8 +48,8 @@ export class PeriodDetailComponent {
|
||||
model = linkedSignal({
|
||||
source: this.item,
|
||||
computation: (item): PeriodFormData => ({
|
||||
validFrom: item.validFrom ? moment(item.validFrom, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
validTill: item.validTill ? moment(item.validTill, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
validFrom: parseDate(item.validFrom),
|
||||
validTill: parseDate(item.validTill),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -100,8 +100,8 @@ export class PeriodDetailComponent {
|
||||
getItem(): Period {
|
||||
const formValue = this.model();
|
||||
const item = this.item();
|
||||
item.validFrom = moment(formValue.validFrom).format('DD-MMM-YYYY');
|
||||
item.validTill = moment(formValue.validTill).format('DD-MMM-YYYY');
|
||||
item.validFrom = formatIsoDate(formValue.validFrom);
|
||||
item.validTill = formatIsoDate(formValue.validTill);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
<mat-datepicker-toggle matSuffix [for]="startDate"></mat-datepicker-toggle>
|
||||
<mat-datepicker #startDate></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Finish Date</mat-label>
|
||||
<input matInput [matDatepicker]="finishDate" [formField]="form.finishDate" autocomplete="off" />
|
||||
@@ -39,9 +40,6 @@
|
||||
[displayWith]="displayFn"
|
||||
(optionSelected)="selected($event)"
|
||||
>
|
||||
@for (product of products(); track product.id) {
|
||||
<mat-option [value]="product">{{ product.name }}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
<button mat-raised-button type="submit" class="flex-auto basis-1-5" color="primary">Show</button>
|
||||
|
||||
@@ -11,9 +11,9 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { Product } from '../core/product';
|
||||
import { ProductSku } from '../core/product-sku';
|
||||
import { ProductService } from '../product/product.service';
|
||||
@@ -23,6 +23,7 @@ export interface ProductLedgerFormData {
|
||||
finishDate: Date;
|
||||
product: Product | string | null;
|
||||
}
|
||||
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { ProductLedger } from './product-ledger';
|
||||
@@ -57,7 +58,6 @@ import { ProductLedgerService } from './product-ledger.service';
|
||||
],
|
||||
})
|
||||
export class ProductLedgerComponent {
|
||||
private route = inject(ActivatedRoute);
|
||||
private router = inject(Router);
|
||||
private toCsv = inject(ToCsvService);
|
||||
private ser = inject(ProductLedgerService);
|
||||
@@ -152,8 +152,8 @@ export class ProductLedgerComponent {
|
||||
model = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (info: ProductLedger): ProductLedgerFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
product: info.product ?? null,
|
||||
}),
|
||||
});
|
||||
@@ -234,8 +234,8 @@ export class ProductLedgerComponent {
|
||||
const product = prodVal && typeof prodVal !== 'string' ? prodVal : undefined;
|
||||
return new ProductLedger({
|
||||
product,
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { ProfitLoss } from './profit-loss';
|
||||
@@ -58,8 +58,8 @@ export class ProfitLossComponent {
|
||||
formModel = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (info: ProfitLoss): ProfitLossFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -133,8 +133,8 @@ export class ProfitLossComponent {
|
||||
const formModel = this.formModel();
|
||||
|
||||
return new ProfitLoss({
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
footer: this.info().footer,
|
||||
body: this.info().body,
|
||||
});
|
||||
|
||||
@@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { PurchaseEntries } from './purchase-entries';
|
||||
@@ -103,8 +103,8 @@ export class PurchaseEntriesComponent {
|
||||
formModel = linkedSignal({
|
||||
source: () => this.info(),
|
||||
computation: (info: PurchaseEntries): PurchaseEntriesFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -137,8 +137,8 @@ export class PurchaseEntriesComponent {
|
||||
const formModel = this.formModel();
|
||||
|
||||
return {
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
body: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { round } from 'mathjs';
|
||||
import moment from 'moment';
|
||||
import { zip } from 'rxjs';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
@@ -24,6 +23,7 @@ import { Account } from '../core/account';
|
||||
import { AccountService } from '../core/account.service';
|
||||
import { Batch } from '../core/batch';
|
||||
import { BatchService } from '../core/batch.service';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { DbFile } from '../core/db-file';
|
||||
import { Inventory } from '../core/inventory';
|
||||
import { User } from '../core/user';
|
||||
@@ -134,7 +134,7 @@ export class PurchaseReturnComponent {
|
||||
);
|
||||
return {
|
||||
isStarred: voucher.isStarred,
|
||||
date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(voucher.date),
|
||||
account: voucher.vendor ?? (this.a() ? new Account({ id: this.a()! }) : null),
|
||||
amount,
|
||||
addRow: {
|
||||
@@ -184,7 +184,7 @@ export class PurchaseReturnComponent {
|
||||
tagsResource = this.tagSer.autocomplete(this.tagQuery);
|
||||
tags = computed(() => this.tagsResource.value() ?? []);
|
||||
|
||||
balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY'));
|
||||
balanceDate = computed(() => formatIsoDate(this.form.date().value()));
|
||||
accountId = computed((): string | null => {
|
||||
const account = this.form.account().value();
|
||||
return typeof account === 'string' ? null : (account?.id ?? null);
|
||||
@@ -240,7 +240,7 @@ export class PurchaseReturnComponent {
|
||||
if (!d) return;
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('d', moment(d).format('DD-MMM-YYYY'));
|
||||
url.searchParams.set('d', formatIsoDate(d));
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
});
|
||||
|
||||
@@ -326,7 +326,7 @@ export class PurchaseReturnComponent {
|
||||
amount: row.amount,
|
||||
batch: row.batch,
|
||||
}),
|
||||
date: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
date: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -372,7 +372,7 @@ export class PurchaseReturnComponent {
|
||||
const m = this.formModel();
|
||||
const base = this.item();
|
||||
const v = new Voucher({ ...base });
|
||||
v.date = moment(m.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(m.date);
|
||||
const account = m.account;
|
||||
v.vendor = !account || typeof account === 'string' ? undefined : account;
|
||||
v.narration = m.narration ?? '';
|
||||
|
||||
@@ -16,13 +16,13 @@ import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { round } from 'mathjs';
|
||||
import moment from 'moment';
|
||||
import { zip } from 'rxjs';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { Account } from '../core/account';
|
||||
import { AccountService } from '../core/account.service';
|
||||
import { Batch } from '../core/batch';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { DbFile } from '../core/db-file';
|
||||
import { Inventory } from '../core/inventory';
|
||||
import { Product } from '../core/product';
|
||||
@@ -139,7 +139,7 @@ export class PurchaseComponent {
|
||||
);
|
||||
return {
|
||||
isStarred: voucher.isStarred,
|
||||
date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(voucher.date),
|
||||
account: voucher.vendor ?? (this.a() ? new Account({ id: this.a()! }) : null),
|
||||
amount,
|
||||
addRow: {
|
||||
@@ -197,7 +197,7 @@ export class PurchaseComponent {
|
||||
return typeof v === 'string' ? v : null;
|
||||
});
|
||||
|
||||
balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY'));
|
||||
balanceDate = computed(() => formatIsoDate(this.form.date().value()));
|
||||
accountId = computed((): string | null => {
|
||||
const account = this.form.account().value();
|
||||
return typeof account === 'string' ? null : (account?.id ?? null);
|
||||
@@ -254,7 +254,7 @@ export class PurchaseComponent {
|
||||
if (!d) return;
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('d', moment(d).format('DD-MMM-YYYY'));
|
||||
url.searchParams.set('d', formatIsoDate(d));
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
});
|
||||
|
||||
@@ -409,7 +409,7 @@ export class PurchaseComponent {
|
||||
const m = this.formModel();
|
||||
const base = this.item();
|
||||
const v = new Voucher({ ...base });
|
||||
v.date = moment(m.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(m.date);
|
||||
const account = m.account;
|
||||
v.vendor = !account || typeof account === 'string' ? undefined : account;
|
||||
v.narration = m.narration ?? '';
|
||||
|
||||
@@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { Purchases } from './purchases';
|
||||
@@ -97,8 +97,8 @@ export class PurchasesComponent {
|
||||
formModel = linkedSignal({
|
||||
source: () => this.info(),
|
||||
computation: (info: Purchases): PurchasesFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -131,8 +131,8 @@ export class PurchasesComponent {
|
||||
const formModel = this.formModel();
|
||||
|
||||
return {
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
body: [],
|
||||
footer: new PurchasesItem(),
|
||||
};
|
||||
|
||||
+7
-7
@@ -13,10 +13,10 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { Account } from '../../core/account';
|
||||
import { AccountService } from '../../core/account.service';
|
||||
import { formatIsoDate, parseDate } from '../../core/date-utils';
|
||||
import { Product } from '../../core/product';
|
||||
import { ProductService } from '../../product/product.service';
|
||||
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
|
||||
@@ -88,9 +88,9 @@ export class RateContractDetailComponent {
|
||||
formModel = linkedSignal({
|
||||
source: this.item,
|
||||
computation: (itemVal): RateContractDetailFormData => ({
|
||||
date: itemVal.date ? moment(itemVal.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
validFrom: itemVal.validFrom ? moment(itemVal.validFrom, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
validTill: itemVal.validTill ? moment(itemVal.validTill, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(itemVal.date),
|
||||
validFrom: parseDate(itemVal.validFrom),
|
||||
validTill: parseDate(itemVal.validTill),
|
||||
account: itemVal.vendor?.name ?? '',
|
||||
addRow: {
|
||||
product: '',
|
||||
@@ -240,9 +240,9 @@ export class RateContractDetailComponent {
|
||||
getItem(): RateContract {
|
||||
const formModel = this.formModel();
|
||||
const item = this.item();
|
||||
item.date = moment(formModel.date).format('DD-MMM-YYYY');
|
||||
item.validFrom = moment(formModel.validFrom).format('DD-MMM-YYYY');
|
||||
item.validTill = moment(formModel.validTill).format('DD-MMM-YYYY');
|
||||
item.date = formatIsoDate(formModel.date);
|
||||
item.validFrom = formatIsoDate(formModel.validFrom);
|
||||
item.validTill = formatIsoDate(formModel.validTill);
|
||||
item.vendor = this.account ?? new Account();
|
||||
item.narration = formModel.narration ?? '';
|
||||
item.items = formModel.items.map(
|
||||
|
||||
@@ -10,8 +10,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { ToCsvService } from '../shared/to-csv.service';
|
||||
@@ -107,9 +107,9 @@ export class RawMaterialCostComponent {
|
||||
|
||||
formModel = linkedSignal({
|
||||
source: () => this.info(),
|
||||
computation: (info): RawMaterialCostFormData => ({
|
||||
startDate: info.startDate ? moment(info.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: info.finishDate ? moment(info.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
computation: (info: RawMaterialCost): RawMaterialCostFormData => ({
|
||||
startDate: parseDate(info.startDate),
|
||||
finishDate: parseDate(info.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -147,8 +147,8 @@ export class RawMaterialCostComponent {
|
||||
const formModel = this.formModel();
|
||||
|
||||
return new RawMaterialCost({
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { round } from 'mathjs';
|
||||
import moment from 'moment';
|
||||
import { zip } from 'rxjs';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { Account } from '../core/account';
|
||||
import { AccountService } from '../core/account.service';
|
||||
import { CostCentre } from '../core/cost-centre';
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { DbFile } from '../core/db-file';
|
||||
import { Journal } from '../core/journal';
|
||||
import { User } from '../core/user';
|
||||
@@ -135,7 +135,7 @@ export class ReceiptComponent {
|
||||
);
|
||||
return {
|
||||
isStarred: voucher.isStarred,
|
||||
date: voucher.date ? moment(voucher.date, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
date: parseDate(voucher.date),
|
||||
receiptAccount: rj?.account?.id ?? this.a() ?? '',
|
||||
receiptAmount,
|
||||
addRow: {
|
||||
@@ -182,7 +182,7 @@ export class ReceiptComponent {
|
||||
tagsResource = this.tagSer.autocomplete(this.tagQuery);
|
||||
tags = computed(() => this.tagsResource.value() ?? []);
|
||||
|
||||
balanceDate = computed(() => moment(this.form.date().value()).format('DD-MMM-YYYY'));
|
||||
balanceDate = computed(() => formatIsoDate(this.form.date().value()));
|
||||
accountId = computed((): string | null => {
|
||||
const account = this.form.addRow.account().value();
|
||||
return typeof account === 'string' ? null : (account?.id ?? null);
|
||||
@@ -209,7 +209,7 @@ export class ReceiptComponent {
|
||||
this.router.navigate(['payment'], {
|
||||
queryParams: {
|
||||
a: this.formModel().receiptAccount,
|
||||
d: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
d: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@@ -223,7 +223,7 @@ export class ReceiptComponent {
|
||||
event.preventDefault();
|
||||
this.router.navigate(['journal'], {
|
||||
queryParams: {
|
||||
d: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
d: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
@@ -253,7 +253,7 @@ export class ReceiptComponent {
|
||||
if (!d) return;
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('d', moment(d).format('DD-MMM-YYYY'));
|
||||
url.searchParams.set('d', formatIsoDate(d));
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
});
|
||||
|
||||
@@ -326,7 +326,7 @@ export class ReceiptComponent {
|
||||
account: row.account,
|
||||
costCentre: row.costCentre,
|
||||
}),
|
||||
date: moment(this.formModel().date).format('DD-MMM-YYYY'),
|
||||
date: formatIsoDate(this.formModel().date),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -365,7 +365,7 @@ export class ReceiptComponent {
|
||||
const m = this.formModel();
|
||||
const base = this.item();
|
||||
const v = new Voucher({ ...base });
|
||||
v.date = moment(m.date).format('DD-MMM-YYYY');
|
||||
v.date = formatIsoDate(m.date);
|
||||
v.narration = m.narration ?? '';
|
||||
|
||||
const existingRj = base.journals.find((x) => x.debit === 1);
|
||||
|
||||
@@ -23,8 +23,8 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../../core/date-utils';
|
||||
import { Product } from '../../core/product';
|
||||
import { ProductSku } from '../../core/product-sku';
|
||||
import { ProductService } from '../../product/product.service';
|
||||
@@ -63,7 +63,7 @@ export interface IngredientFormData {
|
||||
|
||||
export interface RecipeDetailFormData {
|
||||
basics: {
|
||||
date: string;
|
||||
date: Date;
|
||||
source: string;
|
||||
recipeYield: string;
|
||||
product: ProductSku | string | null;
|
||||
@@ -136,7 +136,7 @@ export class RecipeDetailComponent {
|
||||
source: this.item,
|
||||
computation: (itemVal): RecipeDetailFormData => ({
|
||||
basics: {
|
||||
date: itemVal.date,
|
||||
date: parseDate(itemVal.date),
|
||||
source: itemVal.source ?? '',
|
||||
recipeYield: `${itemVal.recipeYield ?? ''}`,
|
||||
product: itemVal.sku ?? null,
|
||||
@@ -488,7 +488,7 @@ export class RecipeDetailComponent {
|
||||
const base = this.item();
|
||||
const item = new Recipe({ ...base });
|
||||
|
||||
item.date = moment(basics.date).format('DD-MMM-YYYY');
|
||||
item.date = formatIsoDate(basics.date);
|
||||
item.source = basics.source ?? '';
|
||||
item.recipeYield = this.math.parseAmount(basics.recipeYield ?? '1', 2);
|
||||
item.garnishing = notes.garnishing ?? '';
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
input,
|
||||
linkedSignal,
|
||||
untracked,
|
||||
afterNextRender,
|
||||
} from '@angular/core';
|
||||
import { Component, computed, effect, inject, input, linkedSignal, untracked, afterNextRender } from '@angular/core';
|
||||
import { form as createForm, FormField, FormRoot } from '@angular/forms/signals';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
@@ -143,11 +134,7 @@ export class RoleDetailComponent {
|
||||
const newPermission = isImplied ? true : currentDirect;
|
||||
const newDisabled = isImplied;
|
||||
|
||||
if (
|
||||
p.direct !== currentDirect ||
|
||||
p.permission !== newPermission ||
|
||||
p.disabled !== newDisabled
|
||||
) {
|
||||
if (p.direct !== currentDirect || p.permission !== newPermission || p.disabled !== newDisabled) {
|
||||
changed = true;
|
||||
return {
|
||||
...p,
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
.right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-accent {
|
||||
color: var(--mat-sys-primary, #0284c7);
|
||||
}
|
||||
|
||||
.header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.title-with-back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.title-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.title-group h2 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.readonly-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
padding: 3px 8px;
|
||||
border-radius: 6px;
|
||||
background: var(--mat-sys-surface-variant, #f1f5f9);
|
||||
color: var(--mat-sys-on-surface-variant, #64748b);
|
||||
border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.day-total-badge {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
background: var(--mat-sys-secondary-container, #e0f2fe);
|
||||
color: var(--mat-sys-on-secondary-container, #0369a1);
|
||||
border: 1px solid rgba(2, 132, 199, 0.2);
|
||||
}
|
||||
|
||||
.day-total-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.day-total-value {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.day-total-sub {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.date-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.date-field {
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.cost-centre-tabs {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.tab-label-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tab-cc-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab-cc-badge {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 2px 7px;
|
||||
border-radius: 12px;
|
||||
background: rgba(2, 132, 199, 0.1);
|
||||
color: var(--mat-sys-primary, #0284c7);
|
||||
}
|
||||
|
||||
/* Voucher Details */
|
||||
.voucher-detail-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.voucher-info-card {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
padding: 14px 18px;
|
||||
border-radius: 10px;
|
||||
background: var(--mat-sys-surface-container-low, #f8fafc);
|
||||
border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--mat-sys-on-surface-variant, #64748b);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.status-pill {
|
||||
display: inline-block;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.status-pill.posted {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.items-table,
|
||||
.journals-table,
|
||||
.summary-table {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Narration */
|
||||
.narration-box {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--mat-sys-surface-container-lowest, #ffffff);
|
||||
border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.narration-label {
|
||||
font-weight: 600;
|
||||
color: var(--mat-sys-on-surface-variant, #64748b);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.narration-text {
|
||||
color: var(--mat-sys-on-surface, #1e293b);
|
||||
}
|
||||
|
||||
/* Journals Section */
|
||||
.journals-section {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--mat-sys-on-surface, #1e293b);
|
||||
}
|
||||
|
||||
/* Metadata Footer */
|
||||
.metadata-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 12px 0 6px;
|
||||
border-top: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.metadata-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--mat-sys-on-surface-variant, #64748b);
|
||||
}
|
||||
|
||||
/* Day Summary Tab */
|
||||
.summary-header-card {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
padding: 16px 20px;
|
||||
border-radius: 10px;
|
||||
background: var(--mat-sys-surface-container-low, #f8fafc);
|
||||
border: 1px solid var(--mat-sys-outline-variant, #e2e8f0);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.summary-metric {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--mat-sys-on-surface-variant, #64748b);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px 16px;
|
||||
text-align: center;
|
||||
color: var(--mat-sys-on-surface-variant, #64748b);
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-state-container h3 {
|
||||
margin: 0 0 6px 0;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: var(--mat-sys-on-surface, #1e293b);
|
||||
}
|
||||
|
||||
.empty-state-container p {
|
||||
margin: 0;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
<header class="row-container space-between header-row">
|
||||
<div class="title-with-back">
|
||||
<button mat-icon-button (click)="goBack()" aria-label="Go back" class="back-button">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
<div class="title-group">
|
||||
<h2>Sale Voucher</h2>
|
||||
<span class="readonly-badge">Read-only</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (dayTotalAmount() > 0) {
|
||||
<div class="day-total-badge" aria-label="Day total sales summary">
|
||||
<span class="day-total-label">Day Total:</span>
|
||||
<span class="day-total-value">{{ dayTotalAmount() | currency: 'INR' }}</span>
|
||||
<span class="day-total-sub">
|
||||
({{ costCentres().length }} cost centre{{ costCentres().length === 1 ? '' : 's' }})
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</header>
|
||||
|
||||
<div class="row-container date-bar">
|
||||
<mat-form-field class="date-field">
|
||||
<mat-label>Business Date</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[matDatepicker]="datePicker"
|
||||
[value]="dayData()?.date"
|
||||
(dateChange)="onDateChange($event)"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (dayResource.isLoading()) {
|
||||
<app-skeleton-loader type="table" [count]="1" />
|
||||
} @else if (dayResource.error(); as err) {
|
||||
<app-error-state [message]="'Failed to load sale vouchers.'" (retryAction)="retry()" />
|
||||
} @else if (costCentres().length === 0) {
|
||||
<div class="empty-state-container" role="status">
|
||||
<mat-icon class="empty-icon">receipt_long</mat-icon>
|
||||
<h3>No Sale Vouchers</h3>
|
||||
<p>There are no sale vouchers recorded for the day.</p>
|
||||
</div>
|
||||
} @else {
|
||||
<mat-tab-group
|
||||
[selectedIndex]="activeCostCentreIndex()"
|
||||
(selectedIndexChange)="activeCostCentreIndex.set($event)"
|
||||
class="cost-centre-tabs"
|
||||
animationDuration="150ms"
|
||||
>
|
||||
@for (tab of costCentres(); track trackByTabId($index, tab)) {
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<div class="tab-label-container">
|
||||
<span class="tab-cc-name">{{ tab.costCentreName }}</span>
|
||||
<span class="tab-cc-badge">{{ tab.amount | currency: 'INR' }}</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<section class="voucher-detail-section" [attr.aria-label]="'Sale voucher for ' + tab.costCentreName">
|
||||
<!-- Voucher Info Banner -->
|
||||
<div class="voucher-info-card">
|
||||
<div class="info-item">
|
||||
<span class="info-label">Cost Centre</span>
|
||||
<span class="info-value font-semibold">{{ tab.costCentreName }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Date</span>
|
||||
<span class="info-value">{{ tab.voucher.date }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Amount</span>
|
||||
<span class="info-value text-accent font-semibold">{{ tab.amount | currency: 'INR' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Status</span>
|
||||
<span class="status-pill posted">Posted</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items Table (mirrors PurchaseComponent table) -->
|
||||
<mat-table
|
||||
[dataSource]="sortedInventories()"
|
||||
matSort
|
||||
(matSortChange)="sortData($event)"
|
||||
class="full-width items-table"
|
||||
aria-label="Sale line items"
|
||||
>
|
||||
<!-- Product Column -->
|
||||
<ng-container matColumnDef="product">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Product</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<span class="product-name">{{ row.batch?.sku?.name || row.batch?.name }}</span>
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="font-semibold">Total</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Quantity Column -->
|
||||
<ng-container matColumnDef="quantity">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">Quantity</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">
|
||||
{{ row.quantity | number: '1.2-2' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right font-semibold">
|
||||
{{ activeVoucherQuantity() | number: '1.2-2' }}
|
||||
</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Rate Column -->
|
||||
<ng-container matColumnDef="rate">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">Rate</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">
|
||||
{{ row.rate | currency: 'INR' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right"></mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Tax Column -->
|
||||
<ng-container matColumnDef="tax">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">Tax</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">
|
||||
{{ row.tax | percent: '1.0-2' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right"></mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Discount Column -->
|
||||
<ng-container matColumnDef="discount">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">Discount</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">
|
||||
{{ row.discount | percent: '1.0-2' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right"></mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Amount Column -->
|
||||
<ng-container matColumnDef="amount">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="right">Amount</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right font-semibold">
|
||||
{{ row.amount | currency: 'INR' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right font-semibold text-accent">
|
||||
{{ tab.amount | currency: 'INR' }}
|
||||
</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="inventoryColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: inventoryColumns"></mat-row>
|
||||
<mat-footer-row *matFooterRowDef="inventoryColumns"></mat-footer-row>
|
||||
</mat-table>
|
||||
|
||||
<!-- Narration -->
|
||||
@if (tab.voucher.narration) {
|
||||
<div class="narration-box">
|
||||
<span class="narration-label">Narration:</span>
|
||||
<span class="narration-text">{{ tab.voucher.narration }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Accounting / Journals Section -->
|
||||
<div class="journals-section">
|
||||
<h4 class="section-title">Accounting Entries</h4>
|
||||
<mat-table
|
||||
[dataSource]="tab.voucher.journals"
|
||||
class="full-width journals-table"
|
||||
aria-label="Accounting journals"
|
||||
>
|
||||
<ng-container matColumnDef="account">
|
||||
<mat-header-cell *matHeaderCellDef>Account</mat-header-cell>
|
||||
<mat-cell *matCellDef="let j">{{ j.account?.name }}</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="font-semibold">Total</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="costCentre">
|
||||
<mat-header-cell *matHeaderCellDef>Cost Centre</mat-header-cell>
|
||||
<mat-cell *matCellDef="let j">{{ j.costCentre?.name }}</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef></mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="debit">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Debit</mat-header-cell>
|
||||
<mat-cell *matCellDef="let j" class="right">
|
||||
{{ j.debit === 1 ? (j.amount | currency: 'INR') : '' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right font-semibold">
|
||||
{{ tab.amount | currency: 'INR' }}
|
||||
</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="credit">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Credit</mat-header-cell>
|
||||
<mat-cell *matCellDef="let j" class="right">
|
||||
{{ j.debit === -1 ? (j.amount | currency: 'INR') : '' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right font-semibold">
|
||||
{{ tab.amount | currency: 'INR' }}
|
||||
</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="journalColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: journalColumns"></mat-row>
|
||||
<mat-footer-row *matFooterRowDef="journalColumns"></mat-footer-row>
|
||||
</mat-table>
|
||||
</div>
|
||||
|
||||
<!-- Metadata Footer (matches PurchaseComponent) -->
|
||||
<footer class="metadata-footer">
|
||||
<span class="metadata-text">
|
||||
Created on <strong>{{ tab.voucher.creationDate | localTime }}</strong> and Last Edited on
|
||||
<strong>{{ tab.voucher.lastEditDate | localTime }}</strong>
|
||||
@if (tab.voucher.user?.name) {
|
||||
by <strong>{{ tab.voucher.user.name }}</strong
|
||||
>.
|
||||
}
|
||||
{{ tab.voucher.posted && tab.voucher.poster ? 'Posted by ' + tab.voucher.poster : '' }}
|
||||
</span>
|
||||
</footer>
|
||||
</section>
|
||||
</mat-tab>
|
||||
}
|
||||
|
||||
<!-- All Cost Centres Overview Tab -->
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<div class="tab-label-container">
|
||||
<mat-icon class="tab-icon">analytics</mat-icon>
|
||||
<span class="tab-cc-name">Day Summary</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<section class="voucher-detail-section" aria-label="Day summary for all cost centres">
|
||||
<div class="summary-header-card">
|
||||
<div class="summary-metric">
|
||||
<span class="metric-label">Business Date</span>
|
||||
<span class="metric-value">{{ dayData()?.date }}</span>
|
||||
</div>
|
||||
<div class="summary-metric">
|
||||
<span class="metric-label">Total Cost Centres</span>
|
||||
<span class="metric-value">{{ costCentres().length }}</span>
|
||||
</div>
|
||||
<div class="summary-metric">
|
||||
<span class="metric-label">Total Items Sold</span>
|
||||
<span class="metric-value">{{ dayTotalItems() | number: '1.0-0' }}</span>
|
||||
</div>
|
||||
<div class="summary-metric">
|
||||
<span class="metric-label">Grand Total Sales</span>
|
||||
<span class="metric-value text-accent font-semibold">{{ dayTotalAmount() | currency: 'INR' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-table [dataSource]="costCentres()" class="full-width summary-table" aria-label="Cost centres summary">
|
||||
<!-- Cost Centre Column -->
|
||||
<ng-container matColumnDef="costCentre">
|
||||
<mat-header-cell *matHeaderCellDef>Cost Centre</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="font-semibold">{{ row.costCentreName }}</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="font-semibold">Grand Total</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Items Count Column -->
|
||||
<ng-container matColumnDef="itemCount">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Line Items</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{ row.itemCount | number: '1.0-0' }}</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right font-semibold">
|
||||
{{ dayTotalItems() | number: '1.0-0' }}
|
||||
</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Amount Column -->
|
||||
<ng-container matColumnDef="amount">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Total Sale</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right font-semibold">
|
||||
{{ row.amount | currency: 'INR' }}
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="right font-semibold text-accent">
|
||||
{{ dayTotalAmount() | currency: 'INR' }}
|
||||
</mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Action Column -->
|
||||
<ng-container matColumnDef="action">
|
||||
<mat-header-cell *matHeaderCellDef class="center">Action</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row; let i = index" class="center">
|
||||
<button mat-button color="primary" (click)="selectCostCentreTab(i)">View Voucher</button>
|
||||
</mat-cell>
|
||||
<mat-footer-cell *matFooterCellDef class="center"></mat-footer-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="summaryColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: summaryColumns"></mat-row>
|
||||
<mat-footer-row *matFooterRowDef="summaryColumns"></mat-footer-row>
|
||||
</mat-table>
|
||||
</section>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
import { CurrencyPipe, DecimalPipe, Location, PercentPipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, computed, inject, input, linkedSignal, signal } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDatepickerInputEvent, MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
|
||||
import { formatIsoDate } from '../core/date-utils';
|
||||
import { Inventory } from '../core/inventory';
|
||||
import { Journal } from '../core/journal';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { LocalTimePipe } from '../shared/local-time.pipe';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { CostCentreTab } from './sale';
|
||||
import { SaleService } from './sale.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sale',
|
||||
templateUrl: './sale.component.html',
|
||||
styleUrls: ['./sale.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
DecimalPipe,
|
||||
PercentPipe,
|
||||
CurrencyPipe,
|
||||
LocalTimePipe,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatTableModule,
|
||||
MatSortModule,
|
||||
MatTabsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatDatepickerModule,
|
||||
SkeletonLoaderComponent,
|
||||
ErrorStateComponent,
|
||||
],
|
||||
})
|
||||
export class SaleComponent {
|
||||
private saleSer = inject(SaleService);
|
||||
private location = inject(Location);
|
||||
|
||||
id = input(null, { transform: (v: string | null | undefined) => v ?? null });
|
||||
dateParam = signal<string | null>(null);
|
||||
|
||||
dayResource = this.saleSer.getDay(this.id, this.dateParam);
|
||||
dayData = computed(() => this.dayResource.value() ?? null);
|
||||
|
||||
vouchers = computed(() => this.dayData()?.vouchers ?? []);
|
||||
|
||||
costCentres = computed<CostCentreTab[]>(() => {
|
||||
return this.vouchers().map((v) => {
|
||||
const amount = v.inventories?.reduce((acc, inv) => acc + (inv.amount || 0), 0) ?? 0;
|
||||
let ccName = v.source?.name || v.journals?.find((j) => j.debit === -1)?.costCentre?.name || '';
|
||||
if (!ccName && v.narration) {
|
||||
const match = v.narration.match(/\(([^)]+)\)/);
|
||||
if (match) {
|
||||
ccName = match[1];
|
||||
}
|
||||
}
|
||||
if (!ccName) {
|
||||
ccName = 'Cost Centre';
|
||||
}
|
||||
return {
|
||||
id: v.id ?? '',
|
||||
costCentreId: v.source?.id ?? '',
|
||||
costCentreName: ccName,
|
||||
amount,
|
||||
itemCount: v.inventories?.length ?? 0,
|
||||
voucher: v,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
activeCostCentreIndex = linkedSignal<CostCentreTab[], number>({
|
||||
source: this.costCentres,
|
||||
computation: (tabs) => {
|
||||
const selectedId = this.dayData()?.selectedVoucherId ?? this.id();
|
||||
if (selectedId) {
|
||||
const idx = tabs.findIndex((t) => t.id === selectedId);
|
||||
if (idx !== -1) return idx;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
|
||||
activeTab = computed<CostCentreTab | null>(() => {
|
||||
const tabs = this.costCentres();
|
||||
const idx = this.activeCostCentreIndex();
|
||||
return idx >= 0 && idx < tabs.length ? tabs[idx] : null;
|
||||
});
|
||||
|
||||
activeVoucher = computed(() => this.activeTab()?.voucher ?? null);
|
||||
|
||||
dayTotalAmount = computed(() => {
|
||||
return this.costCentres().reduce((acc, c) => acc + c.amount, 0);
|
||||
});
|
||||
|
||||
dayTotalItems = computed(() => {
|
||||
return this.costCentres().reduce((acc, c) => acc + c.itemCount, 0);
|
||||
});
|
||||
|
||||
activeVoucherQuantity = computed(() => {
|
||||
const v = this.activeVoucher();
|
||||
return v?.inventories?.reduce((acc, inv) => acc + (inv.quantity || 0), 0) ?? 0;
|
||||
});
|
||||
|
||||
// Sorting
|
||||
sortActive = signal('product');
|
||||
sortDirection = signal<'asc' | 'desc' | ''>('asc');
|
||||
|
||||
sortedInventories = computed<Inventory[]>(() => {
|
||||
const items = [...(this.activeVoucher()?.inventories ?? [])];
|
||||
const active = this.sortActive();
|
||||
const direction = this.sortDirection();
|
||||
if (!active || !direction) return items;
|
||||
|
||||
return items.sort((a, b) => {
|
||||
let valA: string | number;
|
||||
let valB: string | number;
|
||||
switch (active) {
|
||||
case 'product':
|
||||
valA = (a.batch?.sku?.name ?? '').toLowerCase();
|
||||
valB = (b.batch?.sku?.name ?? '').toLowerCase();
|
||||
break;
|
||||
case 'quantity':
|
||||
valA = a.quantity ?? 0;
|
||||
valB = b.quantity ?? 0;
|
||||
break;
|
||||
case 'rate':
|
||||
valA = a.rate ?? 0;
|
||||
valB = b.rate ?? 0;
|
||||
break;
|
||||
case 'tax':
|
||||
valA = a.tax ?? 0;
|
||||
valB = b.tax ?? 0;
|
||||
break;
|
||||
case 'discount':
|
||||
valA = a.discount ?? 0;
|
||||
valB = b.discount ?? 0;
|
||||
break;
|
||||
case 'amount':
|
||||
valA = a.amount ?? 0;
|
||||
valB = b.amount ?? 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
const cmp = valA < valB ? -1 : valA > valB ? 1 : 0;
|
||||
return direction === 'asc' ? cmp : -cmp;
|
||||
});
|
||||
});
|
||||
|
||||
inventoryColumns = ['product', 'quantity', 'rate', 'tax', 'discount', 'amount'];
|
||||
journalColumns = ['account', 'costCentre', 'debit', 'credit'];
|
||||
summaryColumns = ['costCentre', 'itemCount', 'amount', 'action'];
|
||||
|
||||
sortData(sort: Sort): void {
|
||||
this.sortActive.set(sort.active);
|
||||
this.sortDirection.set(sort.direction);
|
||||
}
|
||||
|
||||
onDateChange(event: MatDatepickerInputEvent<Date>): void {
|
||||
if (event.value) {
|
||||
this.dateParam.set(formatIsoDate(event.value));
|
||||
}
|
||||
}
|
||||
|
||||
selectCostCentreTab(index: number): void {
|
||||
this.activeCostCentreIndex.set(index);
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
|
||||
retry(): void {
|
||||
this.dayResource.reload();
|
||||
}
|
||||
|
||||
trackByTabId(_index: number, item: CostCentreTab): string {
|
||||
return item.id;
|
||||
}
|
||||
|
||||
trackByInventoryId(index: number, item: Inventory): string {
|
||||
return item.id ?? `inv-${index}`;
|
||||
}
|
||||
|
||||
trackByJournalId(index: number, item: Journal): string {
|
||||
return item.id ?? `j-${index}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { authGuard } from '../auth/auth-guard.service';
|
||||
import { SaleComponent } from './sale.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: SaleComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: SaleComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,22 @@
|
||||
import { httpResource } from '@angular/common/http';
|
||||
import { Injectable, ResourceRef, Signal } from '@angular/core';
|
||||
|
||||
import { SaleDayResponse } from './sale';
|
||||
|
||||
const url = '/api/sale';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SaleService {
|
||||
getDay(id: Signal<string | null>, date: Signal<string | null>): ResourceRef<SaleDayResponse | undefined> {
|
||||
return httpResource<SaleDayResponse>(() => {
|
||||
const i = id();
|
||||
if (i) {
|
||||
return `${url}/${i}`;
|
||||
}
|
||||
const d = date();
|
||||
return d ? `${url}?d=${d}` : `${url}`;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Voucher } from '../core/voucher';
|
||||
|
||||
export interface SaleDayResponse {
|
||||
date: string;
|
||||
selectedVoucherId: string | null;
|
||||
totalAmount: number;
|
||||
vouchers: Voucher[];
|
||||
}
|
||||
|
||||
export interface CostCentreTab {
|
||||
id: string;
|
||||
costCentreId: string;
|
||||
costCentreName: string;
|
||||
amount: number;
|
||||
itemCount: number;
|
||||
voucher: Voucher;
|
||||
}
|
||||
@@ -68,6 +68,29 @@
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
tr.selected td {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
tr.detail-row {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
tr.detail-row.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.detail-cell {
|
||||
padding: 12px 16px 16px !important;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.vouchers-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<table mat-table [dataSource]="days()" class="full-width">
|
||||
<table mat-table [dataSource]="days()" multiTemplateDataRows class="full-width">
|
||||
<ng-container matColumnDef="expand">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col"><span class="visually-hidden">Expand</span></th>
|
||||
<td mat-cell *matCellDef="let day">
|
||||
@@ -95,59 +95,69 @@
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Amount</th>
|
||||
<td mat-cell *matCellDef="let day" class="number">{{ day.amount | number: '1.2-2' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="expandedDetail">
|
||||
<td mat-cell *matCellDef="let day" [attr.colspan]="displayedColumns.length" class="detail-cell">
|
||||
@if (expanded() === day) {
|
||||
<section class="vouchers-section" aria-label="Vouchers for selected day">
|
||||
@for (voucher of day.vouchers; track trackVoucher($index, voucher)) {
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>{{ voucher.saleCategoryName }} → {{ voucher.costCentre.name }}</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
<span [class]="actionClass(voucher.action)">{{ voucher.action }}</span
|
||||
>
|
||||
<span class="number">{{ voucher.amount | number: '1.2-2' }}</span>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<p>{{ voucher.narration }}</p>
|
||||
<table mat-table [dataSource]="voucher.lines" class="full-width">
|
||||
<ng-container matColumnDef="productName">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Product</th>
|
||||
<td mat-cell *matCellDef="let line">{{ line.name }} ({{ line.units }})</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Quantity</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.quantity }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="rate">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Rate</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.rate | number: '1.2-2' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="taxRate">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Tax</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.taxRate }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="discount">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Discount</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.discount }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Amount</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.amount | number: '1.2-2' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="happyHour">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Happy Hour</th>
|
||||
<td mat-cell *matCellDef="let line">{{ line.isHappyHour ? 'Yes' : '' }}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="lineColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: lineColumns"></tr>
|
||||
</table>
|
||||
</mat-expansion-panel>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns" [class.selected]="expanded() === row"></tr>
|
||||
<tr
|
||||
mat-row
|
||||
*matRowDef="let row; columns: detailColumns"
|
||||
class="detail-row"
|
||||
[class.collapsed]="expanded() !== row"
|
||||
></tr>
|
||||
</table>
|
||||
|
||||
@if (expanded(); as day) {
|
||||
<section aria-label="Vouchers for selected day">
|
||||
@for (voucher of day.vouchers; track trackVoucher($index, voucher)) {
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>{{ voucher.saleCategoryName }} → {{ voucher.costCentre.name }}</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
<span [class]="actionClass(voucher.action)">{{ voucher.action }}</span>
|
||||
<span class="number">{{ voucher.amount | number: '1.2-2' }}</span>
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
<p>{{ voucher.narration }}</p>
|
||||
<table mat-table [dataSource]="voucher.lines" class="full-width">
|
||||
<ng-container matColumnDef="productName">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Product</th>
|
||||
<td mat-cell *matCellDef="let line">{{ line.name }} ({{ line.units }})</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Quantity</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.quantity }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="rate">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Rate</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.rate | number: '1.2-2' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="taxRate">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Tax</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.taxRate }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="discount">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Discount</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.discount }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Amount</th>
|
||||
<td mat-cell *matCellDef="let line" class="number">{{ line.amount | number: '1.2-2' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="happyHour">
|
||||
<th mat-header-cell *matHeaderCellDef scope="col">Happy Hour</th>
|
||||
<td mat-cell *matCellDef="let line">{{ line.isHappyHour ? 'Yes' : '' }}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="lineColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: lineColumns"></tr>
|
||||
</table>
|
||||
</mat-expansion-panel>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
|
||||
<div class="row-container">
|
||||
<button
|
||||
mat-flat-button
|
||||
|
||||
@@ -13,8 +13,8 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, startOfMonth } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { DayPlan, ImportRequest, MappingEntry, PreviewResponse, SaleLine, VoucherPlan } from './sales-import';
|
||||
@@ -55,8 +55,8 @@ export class SalesImportComponent {
|
||||
private snackBar = inject(MatSnackBar);
|
||||
|
||||
model = signal<SalesImportFormData>({
|
||||
startDate: moment().startOf('month').toDate(),
|
||||
finishDate: moment().toDate(),
|
||||
startDate: startOfMonth(new Date()),
|
||||
finishDate: new Date(),
|
||||
});
|
||||
|
||||
form = createForm(this.model);
|
||||
@@ -86,6 +86,7 @@ export class SalesImportComponent {
|
||||
expanded = signal<DayPlan | null>(null);
|
||||
|
||||
displayedColumns = ['expand', 'businessDate', 'action', 'voucherCount', 'amount'];
|
||||
detailColumns = ['expandedDetail'];
|
||||
lineColumns = ['productName', 'quantity', 'rate', 'taxRate', 'discount', 'amount', 'happyHour'];
|
||||
|
||||
constructor() {
|
||||
@@ -102,8 +103,8 @@ export class SalesImportComponent {
|
||||
show(): void {
|
||||
const model = this.model();
|
||||
this.previewRequest.set({
|
||||
startDate: moment(model.startDate).format('YYYY-MM-DD'),
|
||||
finishDate: moment(model.finishDate).format('YYYY-MM-DD'),
|
||||
startDate: formatIsoDate(model.startDate),
|
||||
finishDate: formatIsoDate(model.finishDate),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,8 +131,8 @@ export class SalesImportComponent {
|
||||
import(): void {
|
||||
const model = this.model();
|
||||
const request: ImportRequest = {
|
||||
startDate: moment(model.startDate).format('YYYY-MM-DD'),
|
||||
finishDate: moment(model.finishDate).format('YYYY-MM-DD'),
|
||||
startDate: formatIsoDate(model.startDate),
|
||||
finishDate: formatIsoDate(model.finishDate),
|
||||
};
|
||||
this.importing.set(true);
|
||||
this.ser.execute(request).subscribe({
|
||||
|
||||
@@ -12,12 +12,12 @@ import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { AccountTypeService } from '../account/account-type.service';
|
||||
import { environment } from '../app.environment';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { AccountType } from '../core/account-type';
|
||||
import { endOfMonth, formatDisplayDate, formatIsoDate } from '../core/date-utils';
|
||||
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
|
||||
import { LockInfo } from './lock-info';
|
||||
import { SettingsService } from './settings.service';
|
||||
@@ -87,11 +87,11 @@ export class SettingsComponent {
|
||||
index: 0,
|
||||
lockOlder: false,
|
||||
olderRolling: false,
|
||||
olderDate: moment().date(moment().date(1).daysInMonth()).toDate(),
|
||||
olderDate: endOfMonth(new Date()),
|
||||
olderDays: 0,
|
||||
lockNewer: false,
|
||||
newerRolling: false,
|
||||
newerDate: moment().date(moment().date(1).daysInMonth()).toDate(),
|
||||
newerDate: endOfMonth(new Date()),
|
||||
newerDays: 0,
|
||||
accountTypes: s.accTypes.map(() => ({ accountType: false })),
|
||||
voucherTypes: s.vchTypes.map(() => ({ voucherType: false })),
|
||||
@@ -107,8 +107,8 @@ export class SettingsComponent {
|
||||
lockNewer = computed(() => this.lockInfoModel().lockNewer);
|
||||
newerRolling = computed(() => this.lockInfoModel().newerRolling);
|
||||
|
||||
rebaseDataModel = signal({
|
||||
date: moment(),
|
||||
rebaseDataModel = signal<{ date: Date }>({
|
||||
date: new Date(),
|
||||
});
|
||||
|
||||
rebaseDataForm = createForm(this.rebaseDataModel);
|
||||
@@ -133,13 +133,13 @@ export class SettingsComponent {
|
||||
if (this.lockOlder() && this.olderRolling()) {
|
||||
item.start.days = model.olderDays ?? 0;
|
||||
} else if (this.lockOlder() && !this.olderRolling()) {
|
||||
item.start.date = moment(model.olderDate ?? new Date()).format('DD-MMM-YYYY');
|
||||
item.start.date = formatIsoDate(model.olderDate ?? new Date());
|
||||
}
|
||||
if (this.lockNewer() && this.newerRolling()) {
|
||||
item.finish.days = model.newerDays ?? 0;
|
||||
}
|
||||
if (this.lockNewer() && !this.newerRolling()) {
|
||||
item.finish.date = moment(model.newerDate ?? new Date()).format('DD-MMM-YYYY');
|
||||
item.finish.date = formatIsoDate(model.newerDate ?? new Date());
|
||||
}
|
||||
|
||||
this.accountTypes().forEach((at: AccountType, index: number) => {
|
||||
@@ -156,12 +156,12 @@ export class SettingsComponent {
|
||||
|
||||
const validFrom = model.validFrom;
|
||||
if (validFrom) {
|
||||
item.validFrom = moment(validFrom).format('DD-MMM-YYYY');
|
||||
item.validFrom = formatIsoDate(validFrom);
|
||||
}
|
||||
|
||||
const validTill = model.validTill;
|
||||
if (validTill) {
|
||||
item.validTill = moment(validTill).format('DD-MMM-YYYY');
|
||||
item.validTill = formatIsoDate(validTill);
|
||||
}
|
||||
item.index = model.index ?? 0;
|
||||
|
||||
@@ -183,12 +183,13 @@ export class SettingsComponent {
|
||||
}
|
||||
|
||||
confirmRebase(): void {
|
||||
const rebaseDate = (this.rebaseDataModel().date ?? moment()).format('DD-MMM-YYYY');
|
||||
const rebaseDate = formatIsoDate(this.rebaseDataModel().date ?? new Date());
|
||||
const rebaseDisplay = formatDisplayDate(this.rebaseDataModel().date ?? new Date());
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
width: '250px',
|
||||
data: {
|
||||
title: 'Rebase the database?',
|
||||
content: `Are you sure you want to rebase the database as on ${rebaseDate}? This action is destructive and cannot be undone.`,
|
||||
content: `Are you sure you want to rebase the database as on ${rebaseDisplay}? This action is destructive and cannot be undone.`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import moment from 'moment';
|
||||
import { format, isValid, parse, parseISO } from 'date-fns';
|
||||
|
||||
@Pipe({
|
||||
name: 'localTime',
|
||||
standalone: true,
|
||||
})
|
||||
export class LocalTimePipe implements PipeTransform {
|
||||
transform(value: string): string {
|
||||
if (value === undefined) {
|
||||
transform(value: string | Date | null | undefined): string {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
return moment(value, 'DD-MMM-YYYY HH:mm')
|
||||
.subtract(new Date().getTimezoneOffset(), 'minutes')
|
||||
.format('DD-MMM-YYYY HH:mm');
|
||||
let date: Date;
|
||||
if (value instanceof Date) {
|
||||
date = value;
|
||||
} else {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
return '';
|
||||
}
|
||||
if (/^\d{4}-\d{2}-\d{2}/.test(trimmed)) {
|
||||
const isoString = trimmed.includes('Z') || /[+-]\d{2}:\d{2}$/.test(trimmed) ? trimmed : `${trimmed}Z`;
|
||||
date = parseISO(isoString);
|
||||
} else {
|
||||
date = parse(trimmed, 'dd-MMM-yyyy HH:mm', new Date());
|
||||
}
|
||||
}
|
||||
if (!isValid(date)) {
|
||||
return '';
|
||||
}
|
||||
return format(date, 'dd-MMM-yyyy HH:mm');
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatIsoDate, parseDate } from '../core/date-utils';
|
||||
import { ErrorStateComponent } from '../shared/error-state/error-state.component';
|
||||
import { SkeletonLoaderComponent } from '../shared/skeleton-loader/skeleton-loader.component';
|
||||
import { StockMovement } from './stock-movement';
|
||||
@@ -96,8 +96,8 @@ export class StockMovementComponent {
|
||||
formModel = linkedSignal({
|
||||
source: this.info,
|
||||
computation: (v): StockMovementFormData => ({
|
||||
startDate: v.startDate ? moment(v.startDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
finishDate: v.finishDate ? moment(v.finishDate, 'DD-MMM-YYYY').toDate() : new Date(),
|
||||
startDate: parseDate(v.startDate),
|
||||
finishDate: parseDate(v.finishDate),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -130,8 +130,8 @@ export class StockMovementComponent {
|
||||
const formModel = this.formModel();
|
||||
|
||||
return {
|
||||
startDate: moment(formModel.startDate).format('DD-MMM-YYYY'),
|
||||
finishDate: moment(formModel.finishDate).format('DD-MMM-YYYY'),
|
||||
startDate: formatIsoDate(formModel.startDate),
|
||||
finishDate: formatIsoDate(formModel.finishDate),
|
||||
body: [],
|
||||
};
|
||||
}
|
||||
|
||||
+5
-5
@@ -44,8 +44,8 @@
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Product Group</mat-label>
|
||||
<mat-select [formField]="form.productForm.productGroup">
|
||||
@for (pg of productGroups(); track pg) {
|
||||
<mat-option [value]="pg.id">
|
||||
@for (pg of productGroups(); track pg.id) {
|
||||
<mat-option [value]="pg">
|
||||
{{ pg.name }}
|
||||
</mat-option>
|
||||
}
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
|
||||
<!-- nutritional info shows based on currently selected group in the form -->
|
||||
@if (selectedProductGroup()?.nutritional ?? false) {
|
||||
@if (form.productForm.productGroup().value().nutritional ?? false) {
|
||||
<h3>Nutritional Information</h3>
|
||||
<div class="nutrition-grid">
|
||||
<mat-form-field class="flex-auto">
|
||||
@@ -103,7 +103,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (selectedProductGroup()?.iceCream ?? false) {
|
||||
@if (form.productForm.productGroup().value().iceCream ?? false) {
|
||||
<h3>Ice Cream Information</h3>
|
||||
<div class="row-container">
|
||||
<mat-form-field class="flex-auto">
|
||||
@@ -173,7 +173,7 @@
|
||||
|
||||
<ng-container matColumnDef="productGroup">
|
||||
<mat-header-cell *matHeaderCellDef>Product Group</mat-header-cell>
|
||||
<mat-cell *matCellDef="let p">{{ getPGName(p.productGroup) }}</mat-cell>
|
||||
<mat-cell *matCellDef="let p">{{ p.productGroup.name }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="validFrom">
|
||||
|
||||
+14
-25
@@ -13,8 +13,8 @@ import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatDisplayDate, formatIsoDate, parseDate } from '../../core/date-utils';
|
||||
import { ProductGroup } from '../../core/product-group';
|
||||
import { ProductGroupService } from '../../product-group/product-group.service';
|
||||
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
|
||||
@@ -32,7 +32,7 @@ export interface TemporalProductFormData {
|
||||
fractionUnits: string;
|
||||
isPurchased: boolean;
|
||||
isSold: boolean;
|
||||
productGroup: string;
|
||||
productGroup: ProductGroup;
|
||||
allergen: string;
|
||||
protein: number;
|
||||
carbohydrate: number;
|
||||
@@ -78,7 +78,7 @@ function createEmptyProductForm(): TemporalProductFormData {
|
||||
fractionUnits: '',
|
||||
isPurchased: true,
|
||||
isSold: false,
|
||||
productGroup: '',
|
||||
productGroup: new ProductGroup(),
|
||||
allergen: '',
|
||||
protein: 0,
|
||||
carbohydrate: 0,
|
||||
@@ -170,7 +170,7 @@ export class TemporalProductDetailComponent {
|
||||
fractionUnits: p.fractionUnits || '',
|
||||
isPurchased: p.isPurchased ?? true,
|
||||
isSold: p.isSold ?? false,
|
||||
productGroup: p.productGroup?.id ?? '',
|
||||
productGroup: p.productGroup ?? new ProductGroup(),
|
||||
allergen: p.allergen || '',
|
||||
protein: p.protein || 0,
|
||||
carbohydrate: p.carbohydrate || 0,
|
||||
@@ -185,8 +185,8 @@ export class TemporalProductDetailComponent {
|
||||
otherSolids: p.otherSolids || 0,
|
||||
totalSolids: p.totalSolids || 0,
|
||||
water: p.water || 0,
|
||||
validFrom: p.validFrom ? moment(p.validFrom, 'DD-MMM-YYYY').toDate() : null,
|
||||
validTill: p.validTill ? moment(p.validTill, 'DD-MMM-YYYY').toDate() : null,
|
||||
validFrom: parseDate(p.validFrom, null),
|
||||
validTill: parseDate(p.validTill, null),
|
||||
}));
|
||||
|
||||
const skus: TemporalProductSkuFormData[] = (itemVal.skus || []).map((s) => ({
|
||||
@@ -197,8 +197,8 @@ export class TemporalProductDetailComponent {
|
||||
productYield: s.productYield ?? 1,
|
||||
costPrice: s.costPrice ?? 0,
|
||||
salePrice: s.salePrice ?? 0,
|
||||
validFrom: s.validFrom ? moment(s.validFrom, 'DD-MMM-YYYY').toDate() : null,
|
||||
validTill: s.validTill ? moment(s.validTill, 'DD-MMM-YYYY').toDate() : null,
|
||||
validFrom: parseDate(s.validFrom, null),
|
||||
validTill: parseDate(s.validTill, null),
|
||||
}));
|
||||
|
||||
return {
|
||||
@@ -215,25 +215,14 @@ export class TemporalProductDetailComponent {
|
||||
productGroupsResource = this.pgSer.list();
|
||||
productGroups = computed(() => this.productGroupsResource.value() || []);
|
||||
|
||||
selectedProductGroup = computed(() => {
|
||||
const pgId = this.form.productForm.productGroup().value();
|
||||
return this.productGroups().find((x: ProductGroup) => x.id === pgId);
|
||||
});
|
||||
|
||||
constructor() {
|
||||
afterNextRender(() => {
|
||||
this.form.productForm.name().focusBoundControl();
|
||||
});
|
||||
}
|
||||
|
||||
getPGName(pgId?: string): string {
|
||||
if (!pgId) return '';
|
||||
return this.productGroups().find((x: ProductGroup) => x.id === pgId)?.name ?? '';
|
||||
}
|
||||
|
||||
formatDateOrNull(d: Date | string | null | undefined): string {
|
||||
if (!d) return '';
|
||||
return moment(d).format('DD-MMM-YYYY');
|
||||
return formatDisplayDate(parseDate(d, null));
|
||||
}
|
||||
|
||||
editProduct(row: TemporalProductFormData) {
|
||||
@@ -338,7 +327,7 @@ export class TemporalProductDetailComponent {
|
||||
prod.fractionUnits = p.fractionUnits ?? '';
|
||||
prod.isPurchased = p.isPurchased ?? true;
|
||||
prod.isSold = p.isSold ?? false;
|
||||
prod.productGroup = this.productGroups().find((x: ProductGroup) => x.id === p.productGroup);
|
||||
prod.productGroup = p.productGroup;
|
||||
|
||||
prod.allergen = p.allergen ?? '';
|
||||
prod.protein = p.protein || 0;
|
||||
@@ -356,8 +345,8 @@ export class TemporalProductDetailComponent {
|
||||
prod.totalSolids = p.totalSolids || 0;
|
||||
prod.water = p.water || 0;
|
||||
|
||||
prod.validFrom = p.validFrom ? this.formatDateOrNull(p.validFrom) : null;
|
||||
prod.validTill = p.validTill ? this.formatDateOrNull(p.validTill) : null;
|
||||
prod.validFrom = p.validFrom ? formatIsoDate(p.validFrom) : null;
|
||||
prod.validTill = p.validTill ? formatIsoDate(p.validTill) : null;
|
||||
return prod;
|
||||
});
|
||||
|
||||
@@ -370,8 +359,8 @@ export class TemporalProductDetailComponent {
|
||||
sku.productYield = Number(s.productYield) || 1;
|
||||
sku.costPrice = Number(s.costPrice) || 0;
|
||||
sku.salePrice = Number(s.salePrice) || 0;
|
||||
sku.validFrom = s.validFrom ? this.formatDateOrNull(s.validFrom) : null;
|
||||
sku.validTill = s.validTill ? this.formatDateOrNull(s.validTill) : null;
|
||||
sku.validFrom = s.validFrom ? formatIsoDate(s.validFrom) : null;
|
||||
sku.validTill = s.validTill ? formatIsoDate(s.validTill) : null;
|
||||
return sku;
|
||||
});
|
||||
|
||||
|
||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user