Added: Rate Contract Module. To implement: Checking this during purchase.

This commit is contained in:
2021-09-11 15:43:18 +05:30
parent 20ce1a297e
commit dee053c115
37 changed files with 1391 additions and 216 deletions
@@ -5,10 +5,8 @@ Revises: ad8b2d208492
Create Date: 2021-09-11 05:32:56.683107
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
@@ -0,0 +1,71 @@
"""rate contracts
Revision ID: 071e8f29d257
Revises: 0363f582ab28
Create Date: 2021-09-11 05:57:49.062468
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
from brewman.models.permission import Permission
from sqlalchemy.dialects import postgresql
revision = "071e8f29d257"
down_revision = "0363f582ab28"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.execute(Permission.__table__.insert().values(id="f97589c6-df9f-45d3-874d-4245a4d99ade", name="Rate Contracts"))
op.create_table(
"rate_contracts",
sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("date", sa.Date(), nullable=False),
sa.Column("vendor_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("valid_from", sa.Date(), nullable=True),
sa.Column("valid_till", sa.Date(), nullable=True),
sa.Column("narration", sa.Unicode(length=1000), nullable=False),
sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("creation_date", sa.DateTime(), nullable=False),
sa.Column("last_edit_date", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], name=op.f("fk_rate_contracts_user_id_users")),
sa.ForeignKeyConstraint(["vendor_id"], ["accounts.id"], name=op.f("fk_rate_contracts_vendor_id_accounts")),
sa.PrimaryKeyConstraint("id", name=op.f("pk_rate_contracts")),
)
op.create_index(op.f("ix_rate_contracts_date"), "rate_contracts", ["date"], unique=False)
op.create_table(
"rate_contract_items",
sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("rate_contract_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("product_id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column("cost_price", sa.Numeric(precision=15, scale=2), nullable=False),
sa.ForeignKeyConstraint(
["product_id"], ["products.id"], name=op.f("fk_rate_contract_items_product_id_products")
),
sa.ForeignKeyConstraint(
["rate_contract_id"],
["rate_contracts.id"],
name=op.f("fk_rate_contract_items_rate_contract_id_rate_contracts"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_rate_contract_items")),
sa.UniqueConstraint("rate_contract_id", "product_id", name=op.f("uq_rate_contract_items_rate_contract_id")),
)
op.create_index(
op.f("ix_rate_contract_items_rate_contract_id"), "rate_contract_items", ["rate_contract_id"], unique=False
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_rate_contract_items_rate_contract_id"), table_name="rate_contract_items")
op.drop_table("rate_contract_items")
op.drop_index(op.f("ix_rate_contracts_date"), table_name="rate_contracts")
op.drop_table("rate_contracts")
# ### end Alembic commands ###
@@ -273,7 +273,7 @@ def upgrade():
)
op.execute(permission.delete().where(permission.c.name.in_(["Payment", "Receipt"])))
### end Alembic commands ###
# ### end Alembic commands ###
def downgrade():
+2
View File
@@ -18,6 +18,8 @@ from ..models.login_history import LoginHistory # noqa: F401
from ..models.permission import Permission # noqa: F401
from ..models.product import Product # noqa: F401
from ..models.product_group import ProductGroup # noqa: F401
from ..models.rate_contract import RateContract # noqa: F401
from ..models.rate_contract_item import RateContractItem # noqa: F401
from ..models.recipe import Recipe # noqa: F401
from ..models.recipe_item import RecipeItem # noqa: F401
from ..models.role import Role # noqa: F401
+2
View File
@@ -33,6 +33,7 @@ from .routers import (
product_group,
purchase,
purchase_return,
rate_contract,
rebase,
recipe,
reset_stock,
@@ -76,6 +77,7 @@ app.include_router(attendance_types.router, prefix="/api/attendance-types", tags
app.include_router(employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"])
app.include_router(employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"])
app.include_router(attendance_report.router, prefix="/attendance-report", tags=["attendance"])
app.include_router(rate_contract.router, prefix="/api/rate-contracts", tags=["products"])
app.include_router(cost_centre.router, prefix="/api/cost-centres", tags=["cost-centres"])
app.include_router(employee.router, prefix="/api/employees", tags=["employees"])
+2
View File
@@ -32,6 +32,8 @@ class AccountBase(Base):
journals = relationship("Journal", back_populates="account")
cost_centre = relationship("CostCentre", back_populates="accounts")
rate_contracts = relationship("RateContract", back_populates="vendor")
@property
def __name__(self):
return self.name
+34 -108
View File
@@ -1,124 +1,50 @@
import uuid
from sqlalchemy import (
Boolean,
Column,
ForeignKey,
Integer,
Numeric,
Unicode,
UniqueConstraint,
case,
func,
select,
)
from datetime import datetime
from sqlalchemy import Column, Date, DateTime, ForeignKey, Unicode
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import Session, relationship
from sqlalchemy.orm import relationship
from .meta import Base
class Product(Base):
__tablename__ = "products"
__table_args__ = (UniqueConstraint("name", "units"),)
class RateContract(Base):
__tablename__ = "rate_contracts"
id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
code = Column("code", Integer, unique=True)
name = Column("name", Unicode(255), nullable=False)
units = Column("units", Unicode(255), nullable=False)
fraction = Column("fraction", Numeric(precision=15, scale=5), nullable=False)
fraction_units = Column("fraction_units", Unicode(255), nullable=False)
product_yield = Column("product_yield", Numeric(precision=15, scale=5), nullable=False)
product_group_id = Column(
"product_group_id",
UUID(as_uuid=True),
ForeignKey("product_groups.id"),
nullable=False,
)
account_id = Column("account_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False)
price = Column("cost_price", Numeric(precision=15, scale=2), nullable=False)
sale_price = Column("sale_price", Numeric(precision=15, scale=2), nullable=False)
is_active = Column("is_active", Boolean, nullable=False)
is_fixture = Column("is_fixture", Boolean, nullable=False)
is_purchased = Column("is_purchased", Boolean, nullable=False)
is_sold = Column("is_sold", Boolean, nullable=False)
date = Column("date", Date, nullable=False, index=True)
vendor_id = Column("vendor_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False)
valid_from = Column("valid_from", Date(), nullable=True)
valid_till = Column("valid_till", Date(), nullable=True)
narration = Column("narration", Unicode(1000), nullable=False)
product_group = relationship("ProductGroup", back_populates="products")
batches = relationship("Batch", back_populates="product")
inventories = relationship("Inventory", back_populates="product")
recipes = relationship("Recipe", back_populates="product")
account = relationship("Account", primaryjoin="Account.id==Product.account_id", back_populates="products")
user_id = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
creation_date = Column("creation_date", DateTime(), nullable=False)
last_edit_date = Column("last_edit_date", DateTime(), nullable=False)
user = relationship("User")
vendor = relationship("AccountBase", back_populates="rate_contracts")
items = relationship("RateContractItem", back_populates="rate_contract")
def __init__(
self,
code=None,
name=None,
units=None,
fraction=None,
fraction_units=None,
product_yield=None,
product_group_id=None,
account_id=None,
price=None,
sale_price=None,
is_active=None,
is_purchased=None,
is_sold=None,
date=None,
vendor_id=None,
valid_from=None,
valid_till=None,
narration="",
user_id=None,
creation_date=None,
last_edit_date=None,
id_=None,
is_fixture=False,
):
self.code = code
self.name = name
self.units = units
self.fraction = fraction
self.fraction_units = fraction_units
self.product_yield = product_yield
self.product_group_id = product_group_id
self.account_id = account_id
self.price = price
self.sale_price = sale_price
self.is_active = is_active
self.is_purchased = is_purchased
self.is_sold = is_sold
self.date = date
self.vendor_id = vendor_id
self.valid_from = valid_from
self.valid_till = valid_till
self.narration = narration
self.user_id = user_id
self.creation_date = creation_date or datetime.utcnow()
self.last_edit_date = last_edit_date or datetime.utcnow()
self.id = id_
self.is_fixture = is_fixture
@hybrid_property
def full_name(self):
return f"{self.name} ({self.units})" if self.units else self.name
@full_name.expression
def full_name(cls):
return cls.name + case([(cls.units != "", " (" + cls.units + ")")], else_="")
def create(self, db: Session):
self.code = db.execute(select(func.coalesce(func.max(Product.code), 0) + 1)).scalar_one()
db.add(self)
return self
def can_delete(self, advanced_delete: bool):
if self.is_fixture:
return False, f"{self.name} is a fixture and cannot be edited or deleted."
if self.is_active:
return False, "Product is active"
if len(self.inventories) > 0 and not advanced_delete:
return False, "Product has entries"
return True, ""
@classmethod
def query(cls, q, is_purchased: bool = None, active: bool = None, db: Session = None):
query_ = select(cls)
if active is not None:
query_ = query_.filter(cls.is_active == active)
if is_purchased is not None:
query_ = query_.filter(cls.is_purchased == is_purchased)
if q is not None:
for item in q.split():
if item.strip() != "":
query_ = query_.filter(cls.name.ilike(f"%{item}%"))
return db.execute(query_).scalars().all()
@classmethod
def suspense(cls):
return uuid.UUID("aa79a643-9ddc-4790-ac7f-a41f9efb4c15")
+20 -101
View File
@@ -1,119 +1,38 @@
import uuid
from sqlalchemy import (
Boolean,
Column,
ForeignKey,
Integer,
Numeric,
Unicode,
UniqueConstraint,
case,
func,
select, DateTime, Date,
)
from sqlalchemy import Column, ForeignKey, Numeric, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import Session, relationship
from sqlalchemy.orm import relationship
from .meta import Base
class RateContract(Base):
__tablename__ = "rate_contracts"
__table_args__ = (UniqueConstraint("name", "units"),)
class RateContractItem(Base):
__tablename__ = "rate_contract_items"
__table_args__ = (UniqueConstraint("rate_contract_id", "product_id"),)
id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
rate_contract_id = Column(
"rate_contract_id",
UUID(as_uuid=True),
ForeignKey("rate_contracts.id"),
nullable=False,
index=True,
)
product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False)
price = Column("cost_price", Numeric(precision=15, scale=2), nullable=False)
user = relationship("User", primaryjoin="User.id==Voucher.user_id", cascade=None)
creation_date = Column("creation_date", DateTime(), nullable=False)
last_edit_date = Column("last_edit_date", DateTime(), nullable=False)
vendor_id = Column("vendor_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False)
# product_id = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False)
# price = Column("cost_price", Numeric(precision=15, scale=2), nullable=False)
valid_from = Column("valid_from", Date(), nullable=True)
valid_till = Column("valid_till", Date(), nullable=True)
user = relationship("User")
vendor = relationship("Account", back_populates="rate_contracts")
# batches = relationship("Batch", back_populates="product")
# inventories = relationship("Inventory", back_populates="product")
# recipes = relationship("Recipe", back_populates="product")
# account = relationship("Account", primaryjoin="Account.id==Product.account_id", back_populates="products")
rate_contract = relationship("RateContract", back_populates="items")
product = relationship("Product")
def __init__(
self,
code=None,
name=None,
units=None,
fraction=None,
fraction_units=None,
product_yield=None,
product_group_id=None,
account_id=None,
rate_contract_id=None,
product_id=None,
price=None,
sale_price=None,
is_active=None,
is_purchased=None,
is_sold=None,
id_=None,
is_fixture=False,
):
self.code = code
self.name = name
self.units = units
self.fraction = fraction
self.fraction_units = fraction_units
self.product_yield = product_yield
self.product_group_id = product_group_id
self.account_id = account_id
self.rate_contract_id = rate_contract_id
self.product_id = product_id
self.price = price
self.sale_price = sale_price
self.is_active = is_active
self.is_purchased = is_purchased
self.is_sold = is_sold
self.id = id_
self.is_fixture = is_fixture
@hybrid_property
def full_name(self):
return f"{self.name} ({self.units})" if self.units else self.name
@full_name.expression
def full_name(cls):
return cls.name + case([(cls.units != "", " (" + cls.units + ")")], else_="")
def create(self, db: Session):
self.code = db.execute(select(func.coalesce(func.max(Product.code), 0) + 1)).scalar_one()
db.add(self)
return self
def can_delete(self, advanced_delete: bool):
if self.is_fixture:
return False, f"{self.name} is a fixture and cannot be edited or deleted."
if self.is_active:
return False, "Product is active"
if len(self.inventories) > 0 and not advanced_delete:
return False, "Product has entries"
return True, ""
@classmethod
def query(cls, q, is_purchased: bool = None, active: bool = None, db: Session = None):
query_ = select(cls)
if active is not None:
query_ = query_.filter(cls.is_active == active)
if is_purchased is not None:
query_ = query_.filter(cls.is_purchased == is_purchased)
if q is not None:
for item in q.split():
if item.strip() != "":
query_ = query_.filter(cls.name.ilike(f"%{item}%"))
return db.execute(query_).scalars().all()
@classmethod
def suspense(cls):
return uuid.UUID("aa79a643-9ddc-4790-ac7f-a41f9efb4c15")
+187
View File
@@ -0,0 +1,187 @@
import uuid
from typing import List
from fastapi import APIRouter, HTTPException, Request, Security, status
from sqlalchemy import delete, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..core.session import (
get_date,
get_finish_date,
get_start_date,
set_date,
set_period,
)
from ..db.session import SessionFuture
from ..models.rate_contract import RateContract
from ..models.rate_contract_item import RateContractItem
from ..schemas.product import ProductLink
from ..schemas.rate_contract import AccountLink
from ..schemas.rate_contract import RateContract as RateContractSchema
from ..schemas.rate_contract import RateContractIn as RateContractInSchema
from ..schemas.rate_contract import RateContractItem as RateContractItemSchema
from ..schemas.user import UserToken
from ..schemas.user_link import UserLink
router = APIRouter()
@router.post("", response_model=RateContractSchema)
async def save(
data: RateContractInSchema,
request: Request,
user: UserToken = Security(get_user, scopes=["rate-contracts"]),
) -> RateContractSchema:
try:
with SessionFuture() as db:
item = RateContract(
date=data.date_,
vendor_id=data.vendor.id_,
valid_from=data.valid_from,
valid_till=data.valid_till,
narration=data.narration,
user_id=user.id_,
)
db.add(item)
add_items(item, data.items, db)
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
set_period(data.valid_from.strftime("%d-%b-%Y"), data.valid_till.strftime("%d-%b-%Y"), request.session)
db.commit()
return rate_contract_info(item)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
def add_items(rate_contract: RateContract, items: List[RateContractItemSchema], db: Session) -> None:
for item in items:
rci = RateContractItem(rate_contract_id=rate_contract.id, product_id=item.product.id_, price=item.price)
rate_contract.items.append(rci)
db.add(rci)
@router.put("/{id_}", response_model=RateContractSchema)
async def update_route(
id_: uuid.UUID,
data: RateContractInSchema,
request: Request,
user: UserToken = Security(get_user, scopes=["rate-contracts"]),
) -> RateContractSchema:
try:
with SessionFuture() as db:
item: RateContract = db.execute(select(RateContract).where(RateContract.id == id_)).scalar_one()
item.date = data.date_
item.vendor_id = data.vendor.id_
item.valid_from = data.valid_from
item.valid_till = data.valid_till
item.narration = data.narration
item.user_id = user.id_
update_items(item, data.items, db)
set_date(data.date_.strftime("%d-%b-%Y"), request.session)
set_period(data.valid_from.strftime("%d-%b-%Y"), data.valid_till.strftime("%d-%b-%Y"), request.session)
db.commit()
return rate_contract_info(item)
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
def update_items(rate_contract: RateContract, items: List[RateContractItemSchema], db: Session):
for it in range(len(rate_contract.items), 0, -1):
item = rate_contract.items[it - 1]
for j in range(len(items), 0, -1):
new_item = items[j - 1]
if new_item.id_ == item.id:
item.product_id = new_item.product.id_
item.price = new_item.price
items.remove(new_item)
break
else:
db.delete(item)
rate_contract.items.remove(item)
for item in items:
rci = RateContractItem(rate_contract_id=rate_contract.id, product_id=item.product.id_, price=item.price)
rate_contract.items.append(rci)
db.add(rci)
@router.delete("/{id_}", response_model=RateContractInSchema)
async def delete_route(
id_: uuid.UUID,
request: Request,
user: UserToken = Security(get_user, scopes=["rate-contracts"]),
) -> RateContractInSchema:
with SessionFuture() as db:
db.execute(delete(RateContractItem).where(RateContractItem.rate_contract_id == id_))
db.execute(delete(RateContract).where(RateContract.id == id_))
db.commit()
return rate_contract_blank(request.session)
@router.get("", response_model=RateContractInSchema)
async def show_blank(
request: Request,
user: UserToken = Security(get_user, scopes=["rate-contracts"]),
) -> RateContractInSchema:
return rate_contract_blank(request.session)
@router.get("/list", response_model=List[RateContractSchema])
async def show_list(
user: UserToken = Security(get_user),
) -> List[RateContractSchema]:
with SessionFuture() as db:
return [
rate_contract_info(item)
for item in db.execute(select(RateContract).order_by(RateContract.date)).scalars().all()
]
@router.get("/{id_}", response_model=RateContractSchema)
async def show_id(
id_: uuid.UUID,
user: UserToken = Security(get_user, scopes=["rate-contracts"]),
) -> RateContractSchema:
with SessionFuture() as db:
item: RateContract = db.execute(select(RateContract).where(RateContract.id == id_)).scalar_one()
return rate_contract_info(item)
def rate_contract_info(item: RateContract) -> RateContractSchema:
return RateContractSchema(
id=item.id,
date=item.date,
vendor=AccountLink(id=item.vendor_id, name=item.vendor.name),
validFrom=item.valid_from,
validTill=item.valid_till,
narration=item.narration,
creationDate=item.creation_date,
lastEditDate=item.last_edit_date,
user=UserLink(id=item.user_id, name=item.user.name),
items=[
RateContractItemSchema(
id=i.id,
product=ProductLink(id=i.product_id, name=i.product.full_name),
price=i.price,
)
for i in item.items
],
)
def rate_contract_blank(session: dict) -> RateContractInSchema:
return RateContractInSchema(
date=get_date(session),
validFrom=get_start_date(session),
validTill=get_finish_date(session),
narration="",
items=[],
)
+1 -3
View File
@@ -1,8 +1,7 @@
import uuid
from datetime import date, datetime
from datetime import datetime
from decimal import Decimal
from typing import List, Optional
import brewman.schemas.voucher as output
@@ -16,7 +15,6 @@ from ..core.session import get_first_day
from ..db.session import SessionFuture
from ..models.account import Account
from ..models.account_base import AccountBase
from ..models.account_type import AccountType
from ..models.attendance import Attendance
from ..models.attendance_type import AttendanceType
from ..models.cost_centre import CostCentre
+91
View File
@@ -0,0 +1,91 @@
import uuid
from datetime import date, datetime
from typing import List, Optional
from pydantic import BaseModel, validator
from . import to_camel
from .account import AccountLink
from .rate_contract_item import RateContractItem
from .user_link import UserLink
class RateContractIn(BaseModel):
date_: date
vendor: Optional[AccountLink]
valid_from: date
valid_till: date
narration: str
items: List[RateContractItem]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {
date: lambda v: v.strftime("%d-%b-%Y"),
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"),
}
@validator("date_", pre=True)
def parse_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()
@validator("valid_from", pre=True)
def parse_creation_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y")
@validator("valid_till", pre=True)
def parse_last_edit_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y")
class RateContract(RateContractIn):
id_: uuid.UUID
creation_date: Optional[datetime]
last_edit_date: Optional[datetime]
user: Optional[UserLink]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {
date: lambda v: v.strftime("%d-%b-%Y"),
datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"),
}
# @validator("date_", pre=True)
# def parse_date(cls, value):
# if isinstance(value, date):
# return value
# return datetime.strptime(value, "%d-%b-%Y").date()
#
# @validator("valid_from", pre=True)
# def parse_creation_date(cls, value):
# if isinstance(value, datetime):
# return value
# return datetime.strptime(value, "%d-%b-%Y %H:%M")
#
# @validator("valid_till", pre=True)
# def parse_last_edit_date(cls, value):
# if isinstance(value, datetime):
# return value
# return datetime.strptime(value, "%d-%b-%Y %H:%M")
@validator("creation_date", pre=True)
def parse_creation_date(cls, value):
if isinstance(value, datetime):
return value
return datetime.strptime(value, "%d-%b-%Y %H:%M")
@validator("last_edit_date", pre=True)
def parse_last_edit_date(cls, value):
if isinstance(value, datetime):
return value
return datetime.strptime(value, "%d-%b-%Y %H:%M")
@@ -0,0 +1,17 @@
import uuid
from decimal import Decimal
from typing import Optional
from brewman.schemas import to_camel
from brewman.schemas.product import ProductLink
from pydantic import BaseModel, Field
class RateContractItem(BaseModel):
id_: Optional[uuid.UUID]
product: ProductLink
price: Decimal = Field(ge=0, multiple_of=0.01)
class Config:
alias_generator = to_camel
+5
View File
@@ -68,6 +68,11 @@ const appRoutes: Routes = [
(mod) => mod.EmployeeFunctionsModule,
),
},
{
path: 'rate-contracts',
loadChildren: () =>
import('./rate-contract/rate-contract.module').then((mod) => mod.RateContractModule),
},
{
path: 'roles',
loadChildren: () => import('./role/role.module').then((mod) => mod.RoleModule),
@@ -30,6 +30,7 @@
<a mat-menu-item routerLink="/purchases">Purchases</a>
<a mat-menu-item routerLink="/closing-stock">Closing Stock</a>
<a mat-menu-item routerLink="/stock-movement">Stock Movement</a>
<a mat-menu-item routerLink="/rate-contracts">Rate Contracts</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="productReportMenu">Product Reports</button>
@@ -0,0 +1,13 @@
import { RateContractModule } from './rate-contract.module';
describe('RateContractModule', () => {
let rateContractModule: RateContractModule;
beforeEach(() => {
rateContractModule = new RateContractModule();
});
it('should create an instance', () => {
expect(rateContractModule).toBeTruthy();
});
});
@@ -0,0 +1,16 @@
import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs';
import { RateContractItem } from '../rate-contract-item';
export class RateContractDetailDatasource extends DataSource<RateContractItem> {
constructor(private data: Observable<RateContractItem[]>) {
super();
}
connect(): Observable<RateContractItem[]> {
return this.data;
}
disconnect() {}
}
@@ -0,0 +1,3 @@
.example-card {
max-width: 400px;
}
@@ -0,0 +1,164 @@
<div fxLayout="row" fxFlex="50%" fxLayoutAlign="space-around center" class="example-card">
<mat-card fxFlex>
<mat-card-title-group>
<mat-card-title>Rate Contract</mat-card-title>
</mat-card-title-group>
<mat-card-content>
<form [formGroup]="form" fxLayout="column">
<div
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<mat-form-field fxFlex="40">
<input
matInput
[matDatepicker]="date"
placeholder="Date"
formControlName="date"
autocomplete="off"
#dateElement
/>
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
<mat-datepicker #date></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex="60">
<input
type="text"
matInput
placeholder="Account"
#accountElement
[matAutocomplete]="autoA"
formControlName="account"
autocomplete="off"
/>
<mat-autocomplete
#autoA="matAutocomplete"
autoActiveFirstOption
[displayWith]="displayFn"
(optionSelected)="accountSelected($event)"
>
<mat-option *ngFor="let account of accounts | async" [value]="account">{{
account.name
}}</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
<div
fxLayout="row"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
fxLayoutAlign="space-around start"
>
<mat-form-field fxFlex>
<input
matInput
[matDatepicker]="validFrom"
placeholder="Valid From"
formControlName="validFrom"
autocomplete="off"
/>
<mat-datepicker-toggle matSuffix [for]="validFrom"></mat-datepicker-toggle>
<mat-datepicker #validFrom></mat-datepicker>
</mat-form-field>
<mat-form-field fxFlex>
<input
matInput
[matDatepicker]="validTill"
placeholder="Valid Till"
formControlName="validTill"
autocomplete="off"
/>
<mat-datepicker-toggle matSuffix [for]="validTill"></mat-datepicker-toggle>
<mat-datepicker #validTill></mat-datepicker>
</mat-form-field>
</div>
<div
formGroupName="addRow"
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<mat-form-field fxFlex="60">
<input
type="text"
matInput
placeholder="Product"
#productElement
[matAutocomplete]="autoP"
formControlName="product"
autocomplete="off"
/>
<mat-autocomplete
#autoP="matAutocomplete"
autoActiveFirstOption
[displayWith]="displayFn"
(optionSelected)="productSelected($event)"
>
<mat-option *ngFor="let product of products | async" [value]="product">{{
product.name
}}</mat-option>
</mat-autocomplete>
</mat-form-field>
<mat-form-field fxFlex="20">
<mat-label>Price</mat-label>
<input
type="text"
matInput
placeholder="Price"
formControlName="price"
autocomplete="off"
/>
</mat-form-field>
<button mat-raised-button color="primary" (click)="addRow()" fxFlex="20">Add</button>
</div>
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
<!-- Product Column -->
<ng-container matColumnDef="product">
<mat-header-cell *matHeaderCellDef>Product</mat-header-cell>
<mat-cell *matCellDef="let row">{{ row.product.name }}</mat-cell>
</ng-container>
<!-- Price Column -->
<ng-container matColumnDef="price">
<mat-header-cell *matHeaderCellDef class="right">Price</mat-header-cell>
<mat-cell *matCellDef="let row" class="right">{{
row.price | currency: 'INR'
}}</mat-cell>
</ng-container>
<!-- Action Column -->
<ng-container matColumnDef="action">
<mat-header-cell *matHeaderCellDef class="center">Action</mat-header-cell>
<mat-cell *matCellDef="let row" class="center">
<button mat-icon-button tabindex="-1" color="warn" (click)="deleteRow(row)">
<mat-icon>delete</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
<mat-form-field>
<mat-label>Narration</mat-label>
<textarea
matInput
matAutosizeMinRows="5"
placeholder="Narration"
formControlName="narration"
></textarea>
</mat-form-field>
</form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" (click)="save()">Save</button>
<button mat-raised-button color="warn" (click)="confirmDelete()">Delete</button>
</mat-card-actions>
</mat-card>
</div>
@@ -0,0 +1,29 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { RateContractDetailComponent } from './rate-contract-detail.component';
describe('RateContractDetailComponent', () => {
let component: RateContractDetailComponent;
let fixture: ComponentFixture<RateContractDetailComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule, RouterTestingModule],
declarations: [RateContractDetailComponent],
}).compileComponents();
}),
);
beforeEach(() => {
fixture = TestBed.createComponent(RateContractDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,219 @@
import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import * as moment from 'moment';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators';
import { Account } from '../../core/account';
import { AccountService } from '../../core/account.service';
import { Product } from '../../core/product';
import { ToasterService } from '../../core/toaster.service';
import { ProductService } from '../../product/product.service';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { MathService } from '../../shared/math.service';
import { RateContract } from '../rate-contract';
import { RateContractItem } from '../rate-contract-item';
import { RateContractService } from '../rate-contract.service';
import { RateContractDetailDatasource } from './rate-contract-detail-datasource';
@Component({
selector: 'app-rate-contract-detail',
templateUrl: './rate-contract-detail.component.html',
styleUrls: ['./rate-contract-detail.component.css'],
})
export class RateContractDetailComponent implements OnInit, AfterViewInit {
@ViewChild('accountElement', { static: true }) accountElement?: ElementRef;
@ViewChild('productElement', { static: true }) productElement?: ElementRef;
public itemsObservable = new BehaviorSubject<RateContractItem[]>([]);
dataSource: RateContractDetailDatasource = new RateContractDetailDatasource(this.itemsObservable);
form: FormGroup;
item: RateContract = new RateContract();
product: Product | null = null;
displayedColumns = ['product', 'price', 'action'];
accounts: Observable<Account[]>;
products: Observable<Product[]>;
constructor(
private route: ActivatedRoute,
private router: Router,
private fb: FormBuilder,
private toaster: ToasterService,
private dialog: MatDialog,
private math: MathService,
private ser: RateContractService,
private productSer: ProductService,
private accountSer: AccountService,
) {
this.form = this.fb.group({
date: '',
account: '',
validFrom: '',
validTill: '',
addRow: this.fb.group({
product: '',
price: '',
}),
narration: '',
});
this.accounts = (this.form.get('account') as FormControl).valueChanges.pipe(
startWith(null),
map((x) => (x !== null && x.length >= 1 ? x : null)),
debounceTime(150),
distinctUntilChanged(),
switchMap((x) => (x === null ? observableOf([]) : this.accountSer.autocomplete(x))),
);
// Listen to Product Autocomplete Change
this.products = (
(this.form.get('addRow') as FormControl).get('product') as FormControl
).valueChanges.pipe(
startWith(null),
map((x) => (x !== null && x.length >= 1 ? x : null)),
debounceTime(150),
distinctUntilChanged(),
switchMap((x) => (x === null ? observableOf([]) : this.productSer.autocomplete(x))),
);
}
ngOnInit() {
this.route.data.subscribe((value) => {
const data = value as { item: RateContract };
this.loadItem(data.item);
});
}
loadItem(item: RateContract) {
this.item = item;
this.form.setValue({
date: moment(this.item.date, 'DD-MMM-YYYY').toDate(),
validFrom: moment(this.item.validFrom, 'DD-MMM-YYYY').toDate(),
validTill: moment(this.item.validTill, 'DD-MMM-YYYY').toDate(),
account: this.item.vendor,
addRow: {
product: '',
price: '',
},
narration: this.item.narration,
});
this.dataSource = new RateContractDetailDatasource(this.itemsObservable);
this.updateView();
}
ngAfterViewInit() {
setTimeout(() => {
if (this.accountElement) {
this.accountElement.nativeElement.focus();
}
}, 0);
}
addRow() {
const formValue = (this.form.get('addRow') as FormControl).value;
const price = this.math.parseAmount(formValue.price, 2);
if (this.product === null || price <= 0) {
return;
}
const oldFiltered = this.item.items.filter(
(x) => x.product.id === (this.product as Product).id,
);
if (oldFiltered.length) {
this.toaster.show('Danger', 'Product already added');
return;
}
this.item.items.push(
new RateContractItem({
price,
product: this.product,
}),
);
this.resetAddRow();
this.updateView();
}
resetAddRow() {
(this.form.get('addRow') as FormControl).reset({
product: null,
price: '',
});
this.product = null;
setTimeout(() => {
if (this.productElement) {
this.productElement.nativeElement.focus();
}
}, 0);
}
displayFn(item?: Account | Product): string {
return item ? item.name : '';
}
updateView() {
this.itemsObservable.next(this.item.items);
}
accountSelected(event: MatAutocompleteSelectedEvent): void {
(this.form.get('account') as FormControl).setValue(event.option.value);
}
productSelected(event: MatAutocompleteSelectedEvent): void {
this.product = event.option.value;
}
deleteRow(row: RateContractItem) {
this.item.items.splice(this.item.items.indexOf(row), 1);
this.updateView();
}
save() {
this.ser.saveOrUpdate(this.getItem()).subscribe(
() => {
this.toaster.show('Success', '');
this.router.navigateByUrl('/rate-contracts');
},
(error) => {
this.toaster.show('Danger', error);
},
);
}
delete() {
this.ser.delete(this.item.id as string).subscribe(
() => {
this.toaster.show('Success', '');
this.router.navigateByUrl('/rate-contracts');
},
(error) => {
this.toaster.show('Danger', error);
},
);
}
confirmDelete(): void {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: { title: 'Delete RateContract?', content: 'Are you sure? This cannot be undone.' },
});
dialogRef.afterClosed().subscribe((result: boolean) => {
if (result) {
this.delete();
}
});
}
getItem(): RateContract {
const formModel = this.form.value;
this.item.date = moment(formModel.date).format('DD-MMM-YYYY');
this.item.validFrom = moment(formModel.validFrom).format('DD-MMM-YYYY');
this.item.validTill = moment(formModel.validTill).format('DD-MMM-YYYY');
this.item.vendor = formModel.account;
this.item.narration = formModel.narration;
return this.item;
}
}
@@ -0,0 +1,13 @@
import { Product } from '../core/product';
export class RateContractItem {
id: string | undefined;
product: Product;
price: number;
public constructor(init?: Partial<RateContractItem>) {
this.product = new Product();
this.price = 0;
Object.assign(this, init);
}
}
@@ -0,0 +1,21 @@
import { HttpClientModule } from '@angular/common/http';
import { inject, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { RateContractListResolver } from './rate-contract-list-resolver.service';
describe('RateContractListResolver', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule, RouterTestingModule],
providers: [RateContractListResolver],
});
});
it('should be created', inject(
[RateContractListResolver],
(service: RateContractListResolver) => {
expect(service).toBeTruthy();
},
));
});
@@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable } from 'rxjs/internal/Observable';
import { RateContract } from './rate-contract';
import { RateContractService } from './rate-contract.service';
@Injectable({
providedIn: 'root',
})
export class RateContractListResolver implements Resolve<RateContract[]> {
constructor(private ser: RateContractService) {}
resolve(): Observable<RateContract[]> {
return this.ser.list();
}
}
@@ -0,0 +1,74 @@
import { DataSource } from '@angular/cdk/collections';
import { EventEmitter } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatSort, Sort } from '@angular/material/sort';
import { merge, Observable, of as observableOf } from 'rxjs';
import { map } from 'rxjs/operators';
import { RateContract } from '../rate-contract';
/** Simple sort comparator for example ID/Name columns (for client-side sorting). */
const compare = (a: string | number, b: string | number, isAsc: boolean) =>
(a < b ? -1 : 1) * (isAsc ? 1 : -1);
export class RateContractListDatasource extends DataSource<RateContract> {
constructor(
public data: RateContract[],
private paginator?: MatPaginator,
private sort?: MatSort,
) {
super();
}
connect(): Observable<RateContract[]> {
const dataMutations: (
| Observable<RateContract[]>
| EventEmitter<PageEvent>
| EventEmitter<Sort>
)[] = [observableOf(this.data)];
if (this.paginator) {
dataMutations.push((this.paginator as MatPaginator).page);
}
if (this.sort) {
dataMutations.push((this.sort as MatSort).sortChange);
}
// Set the paginators length
if (this.paginator) {
this.paginator.length = this.data.length;
}
return merge(...dataMutations).pipe(
map(() => this.getPagedData(this.getSortedData([...this.data]))),
);
}
disconnect() {}
private getPagedData(data: RateContract[]) {
if (this.paginator === undefined) {
return data;
}
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
return data.splice(startIndex, this.paginator.pageSize);
}
private getSortedData(data: RateContract[]) {
if (this.sort === undefined) {
return data;
}
if (!this.sort.active || this.sort.direction === '') {
return data;
}
const sort = this.sort as MatSort;
return data.sort((a, b) => {
const isAsc = sort.direction === 'asc';
switch (sort.active) {
case 'vendor':
return compare(a.vendor.name, b.vendor.name, isAsc);
default:
return 0;
}
});
}
}
@@ -0,0 +1,52 @@
<mat-card>
<mat-card-title-group>
<mat-card-title>Rate Contracts</mat-card-title>
<a mat-button [routerLink]="['/rate-contracts', 'new']">
<mat-icon>add_box</mat-icon>
Add
</a>
</mat-card-title-group>
<mat-card-content>
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
<!-- Vendor Column -->
<ng-container matColumnDef="vendor">
<mat-header-cell *matHeaderCellDef mat-sort-header>Vendor</mat-header-cell>
<mat-cell *matCellDef="let row"
><a [routerLink]="['/rate-contracts', row.id]"
>{{ row.vendor.name }} // {{ row.date }}</a
></mat-cell
>
</ng-container>
<!-- Validity Column -->
<ng-container matColumnDef="validity">
<mat-header-cell *matHeaderCellDef mat-sort-header>Validity</mat-header-cell>
<mat-cell *matCellDef="let row"> {{ row.validFrom }} - {{ row.validTill }} </mat-cell>
</ng-container>
<!-- Products Column -->
<ng-container matColumnDef="products">
<mat-header-cell *matHeaderCellDef mat-sort-header>Products</mat-header-cell>
<mat-cell *matCellDef="let row">
<ul>
<li *ngFor="let item of row.items">
{{ item.product.name }} @ {{ item.price | currency: 'INR' }}
</li>
</ul>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
<mat-paginator
#paginator
[length]="dataSource.data.length"
[pageIndex]="0"
[pageSize]="50"
[pageSizeOptions]="[25, 50, 100, 250]"
>
</mat-paginator>
</mat-card-content>
</mat-card>
@@ -0,0 +1,24 @@
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { RateContractListComponent } from './rate-contract-list.component';
describe('RateContractListComponent', () => {
let component: RateContractListComponent;
let fixture: ComponentFixture<RateContractListComponent>;
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [RateContractListComponent],
}).compileComponents();
fixture = TestBed.createComponent(RateContractListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should compile', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,33 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { ActivatedRoute } from '@angular/router';
import { RateContract } from '../rate-contract';
import { RateContractListDatasource } from './rate-contract-list-datasource';
@Component({
selector: 'app-rate-contract-list',
templateUrl: './rate-contract-list.component.html',
styleUrls: ['./rate-contract-list.component.css'],
})
export class RateContractListComponent implements OnInit {
@ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator;
@ViewChild(MatSort, { static: true }) sort?: MatSort;
list: RateContract[] = [];
dataSource: RateContractListDatasource = new RateContractListDatasource(this.list);
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns = ['vendor', 'validity', 'products'];
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.data.subscribe((value) => {
const data = value as { list: RateContract[] };
this.list = data.list;
});
this.dataSource = new RateContractListDatasource(this.list, this.paginator, this.sort);
}
}
@@ -0,0 +1,18 @@
import { HttpClientModule } from '@angular/common/http';
import { inject, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { RateContractResolver } from './rate-contract-resolver.service';
describe('RateContractResolver', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule, RouterTestingModule],
providers: [RateContractResolver],
});
});
it('should be created', inject([RateContractResolver], (service: RateContractResolver) => {
expect(service).toBeTruthy();
}));
});
@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import { Observable } from 'rxjs/internal/Observable';
import { RateContract } from './rate-contract';
import { RateContractService } from './rate-contract.service';
@Injectable({
providedIn: 'root',
})
export class RateContractResolver implements Resolve<RateContract> {
constructor(private ser: RateContractService) {}
resolve(route: ActivatedRouteSnapshot): Observable<RateContract> {
const id = route.paramMap.get('id');
return this.ser.get(id);
}
}
@@ -0,0 +1,13 @@
import { RateContractRoutingModule } from './rate-contract-routing.module';
describe('RateContractRoutingModule', () => {
let rateContractRoutingModule: RateContractRoutingModule;
beforeEach(() => {
rateContractRoutingModule = new RateContractRoutingModule();
});
it('should create an instance', () => {
expect(rateContractRoutingModule).toBeTruthy();
});
});
@@ -0,0 +1,53 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '../auth/auth-guard.service';
import { RateContractDetailComponent } from './rate-contract-detail/rate-contract-detail.component';
import { RateContractListResolver } from './rate-contract-list-resolver.service';
import { RateContractListComponent } from './rate-contract-list/rate-contract-list.component';
import { RateContractResolver } from './rate-contract-resolver.service';
const rateContractRoutes: Routes = [
{
path: '',
component: RateContractListComponent,
canActivate: [AuthGuard],
data: {
permission: 'Rate Contracts',
},
resolve: {
list: RateContractListResolver,
},
},
{
path: 'new',
component: RateContractDetailComponent,
canActivate: [AuthGuard],
data: {
permission: 'Rate Contracts',
},
resolve: {
item: RateContractResolver,
},
},
{
path: ':id',
component: RateContractDetailComponent,
canActivate: [AuthGuard],
data: {
permission: 'Rate Contracts',
},
resolve: {
item: RateContractResolver,
},
},
];
@NgModule({
imports: [CommonModule, RouterModule.forChild(rateContractRoutes)],
exports: [RouterModule],
providers: [RateContractListResolver, RateContractResolver],
})
export class RateContractRoutingModule {}
@@ -0,0 +1,71 @@
import { CdkTableModule } from '@angular/cdk/table';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ReactiveFormsModule } from '@angular/forms';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import {
DateAdapter,
MAT_DATE_FORMATS,
MAT_DATE_LOCALE,
MatNativeDateModule,
} from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { SharedModule } from '../shared/shared.module';
import { RateContractDetailComponent } from './rate-contract-detail/rate-contract-detail.component';
import { RateContractListComponent } from './rate-contract-list/rate-contract-list.component';
import { RateContractRoutingModule } from './rate-contract-routing.module';
export const MY_FORMATS = {
parse: {
dateInput: 'DD-MMM-YYYY',
},
display: {
dateInput: 'DD-MMM-YYYY',
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'DD-MMM-YYYY',
monthYearA11yLabel: 'MMM YYYY',
},
};
@NgModule({
imports: [
CommonModule,
CdkTableModule,
FlexLayoutModule,
MatButtonModule,
MatCardModule,
MatCheckboxModule,
MatDividerModule,
MatIconModule,
MatInputModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatSortModule,
MatTableModule,
ReactiveFormsModule,
SharedModule,
RateContractRoutingModule,
MatDatepickerModule,
MatAutocompleteModule,
],
declarations: [RateContractListComponent, RateContractDetailComponent],
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
],
})
export class RateContractModule {}
@@ -0,0 +1,17 @@
import { HttpClientModule } from '@angular/common/http';
import { inject, TestBed } from '@angular/core/testing';
import { RateContractService } from './rate-contract.service';
describe('RateContractService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
providers: [RateContractService],
});
});
it('should be created', inject([RateContractService], (service: RateContractService) => {
expect(service).toBeTruthy();
}));
});
@@ -0,0 +1,58 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { catchError } from 'rxjs/operators';
import { ErrorLoggerService } from '../core/error-logger.service';
import { RateContract } from './rate-contract';
const url = '/api/rate-contracts';
const serviceName = 'RateContractService';
@Injectable({
providedIn: 'root',
})
export class RateContractService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {}
get(id: string | null): Observable<RateContract> {
const getUrl: string = id === null ? `${url}` : `${url}/${id}`;
return this.http
.get<RateContract>(getUrl)
.pipe(
catchError(this.log.handleError(serviceName, `get id=${id}`)),
) as Observable<RateContract>;
}
list(): Observable<RateContract[]> {
return this.http
.get<RateContract[]>(`${url}/list`)
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<RateContract[]>;
}
save(rateContract: RateContract): Observable<RateContract> {
return this.http
.post<RateContract>(`${url}`, rateContract)
.pipe(catchError(this.log.handleError(serviceName, 'save'))) as Observable<RateContract>;
}
update(rateContract: RateContract): Observable<RateContract> {
return this.http
.put<RateContract>(`${url}/${rateContract.id}`, rateContract)
.pipe(catchError(this.log.handleError(serviceName, 'update'))) as Observable<RateContract>;
}
saveOrUpdate(rateContract: RateContract): Observable<RateContract> {
if (!rateContract.id) {
return this.save(rateContract);
}
return this.update(rateContract);
}
delete(id: string): Observable<RateContract> {
return this.http
.delete<RateContract>(`${url}/${id}`)
.pipe(catchError(this.log.handleError(serviceName, 'delete'))) as Observable<RateContract>;
}
}
@@ -0,0 +1,30 @@
import { Account } from '../core/account';
import { User } from '../core/user';
import { RateContractItem } from './rate-contract-item';
export class RateContract {
id: string | undefined;
date: string;
vendor: Account;
validFrom: string;
validTill: string;
narration: string;
creationDate: string;
lastEditDate: string;
user: User;
items: RateContractItem[];
public constructor(init?: Partial<RateContract>) {
this.date = '';
this.vendor = new Account();
this.validFrom = '';
this.validTill = '';
this.narration = '';
this.creationDate = '';
this.lastEditDate = '';
this.user = new User();
this.items = [];
Object.assign(this, init);
}
}