From 85a9c8714d2697e2a623dc9b38bc689b79a4db60 Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Mon, 29 Dec 2025 10:14:28 +0330 Subject: [PATCH] feat: implement invoice payments component and integrate with supplier invoices --- .../components/filters/filters.component.ts | 2 - src/app/modules/cardex/store/main.store.ts | 41 ++++++---- .../bankAccounts/select.component.ts | 4 + .../inventories/constants/routes/index.ts | 23 +++--- .../inventories/models/bankAccounts.io.ts | 21 ++++++ .../inventories/store/inventory.store.ts | 3 + .../inventories/views/purchase.component.html | 4 + .../inventories/views/purchase.component.ts | 22 ++++++ .../inventories/views/single.component.html | 11 ++- .../purchase-receipt-template.component.html | 8 +- .../purchase-receipt-template.component.ts | 11 ++- .../invoices/payments.component.html | 5 ++ .../components/invoices/payments.component.ts | 75 +++++++++++++++++++ .../components/ledger/ledger.component.html | 4 +- .../suppliers/constants/apiRoutes/invoices.ts | 2 + .../modules/suppliers/models/invoices.io.ts | 18 ++++- src/app/modules/suppliers/models/types.ts | 16 ++++ .../suppliers/services/invoices.service.ts | 11 +++ .../suppliers/views/invoice.component.html | 1 + .../suppliers/views/invoice.component.ts | 3 + .../suppliers/views/invoices.component.html | 10 ++- .../components/input/input.component.ts | 2 - .../page-data-list.component.html | 16 +++- .../pageDataList/page-data-list.component.ts | 2 +- 24 files changed, 268 insertions(+), 47 deletions(-) create mode 100644 src/app/modules/inventories/views/purchase.component.html create mode 100644 src/app/modules/inventories/views/purchase.component.ts create mode 100644 src/app/modules/suppliers/components/invoices/payments.component.html create mode 100644 src/app/modules/suppliers/components/invoices/payments.component.ts diff --git a/src/app/modules/cardex/components/filters/filters.component.ts b/src/app/modules/cardex/components/filters/filters.component.ts index 4f4a422..de2bb48 100644 --- a/src/app/modules/cardex/components/filters/filters.component.ts +++ b/src/app/modules/cardex/components/filters/filters.component.ts @@ -63,8 +63,6 @@ export class CardexFiltersComponent { this.store.setDefaultInventory(inventory); } setDefaultProduct(product: IProductResponse) { - console.log('setDefaultProduct'); - this.store.setDefaultProduct(product); } } diff --git a/src/app/modules/cardex/store/main.store.ts b/src/app/modules/cardex/store/main.store.ts index f603844..55765ad 100644 --- a/src/app/modules/cardex/store/main.store.ts +++ b/src/app/modules/cardex/store/main.store.ts @@ -17,6 +17,7 @@ export interface CardexState extends PaginatedState { selectedProduct: Maybe; variant: 'inventory' | 'product' | 'general'; canChangeVariant: boolean; + cardexTitle: string; } @Injectable({ @@ -63,6 +64,7 @@ export class CardexStore extends PaginatedStore { selectedProduct: null, variant: 'general', canChangeVariant: true, + cardexTitle: 'کاردکس', }); this.initial(); } @@ -70,18 +72,7 @@ export class CardexStore extends PaginatedStore { readonly filters = computed(() => this._state().filters); readonly variant = computed(() => this._state().variant); readonly canChangeVariant = computed(() => this._state().canChangeVariant); - readonly cardexTitle = computed(() => { - switch (this._state().variant) { - case 'product': - return `کاردکس کالای ${this._state().selectedProduct?.name}`; - case 'inventory': - return `کاردکس انبار ${this._state().selectedInventory?.name}`; - default: - return `کاردکس ${this._state().selectedProduct ? 'کالای ' + this._state().selectedProduct?.name : ''} ${ - this._state().selectedInventory ? 'در انبار ' + this._state().selectedInventory?.name : '' - }`; - } - }); + readonly cardexTitle = computed(() => this._state().cardexTitle); /** * Reset state to initial values @@ -109,6 +100,7 @@ export class CardexStore extends PaginatedStore { selectedProduct: null, variant: 'general', canChangeVariant: this.state().canChangeVariant, + cardexTitle: 'کاردکس', }); } @@ -155,8 +147,6 @@ export class CardexStore extends PaginatedStore { updateFilter(filters: ICardexRequestPayload) { this.updateVariant(); - console.log('filters'); - console.log(filters); const cleanedFilters = this.validateFilters() || {}; this.router @@ -169,6 +159,7 @@ export class CardexStore extends PaginatedStore { console.log(err); }); this.patchState({ filters }); + this.updateCardexTitle(); } setProduct(product: Maybe) { @@ -184,10 +175,12 @@ export class CardexStore extends PaginatedStore { setDefaultInventory(inventory: ICardexInventorySummary) { this.patchState({ selectedInventory: inventory }); + this.updateCardexTitle(); } setDefaultProduct(product: ICardexProductSummary) { this.patchState({ selectedProduct: product }); + this.updateCardexTitle(); } private updateVariant() { @@ -201,7 +194,27 @@ export class CardexStore extends PaginatedStore { } else if (productIsSelected) { variant = 'product'; } + this.updateCardexTitle(); this.patchState({ variant }); } + + updateCardexTitle() { + let title = ''; + switch (this._state().variant) { + case 'product': + title = `کاردکس کالای ${this._state().selectedProduct?.name}`; + break; + case 'inventory': + title = `کاردکس انبار ${this._state().selectedInventory?.name}`; + break; + default: + title = `کاردکس ${this._state().selectedProduct ? 'کالای ' + this._state().selectedProduct?.name : ''} ${ + this._state().selectedInventory ? 'در انبار ' + this._state().selectedInventory?.name : '' + }`; + break; + } + + this.patchState({ cardexTitle: title }); + } } diff --git a/src/app/modules/inventories/components/bankAccounts/select.component.ts b/src/app/modules/inventories/components/bankAccounts/select.component.ts index 3e314a7..7481c22 100644 --- a/src/app/modules/inventories/components/bankAccounts/select.component.ts +++ b/src/app/modules/inventories/components/bankAccounts/select.component.ts @@ -29,6 +29,10 @@ export class InventoryBankAccountSelectComponent extends AbstractSelectComponent constructor(private service: InventoryBankAccountsService) { super(); + } + + ngOnInit() { + console.log(this.inventoryId); this.getData(); } diff --git a/src/app/modules/inventories/constants/routes/index.ts b/src/app/modules/inventories/constants/routes/index.ts index 87adf7c..fa5dc06 100644 --- a/src/app/modules/inventories/constants/routes/index.ts +++ b/src/app/modules/inventories/constants/routes/index.ts @@ -1,15 +1,7 @@ import { NamedRoutes } from '@/core'; import { Routes } from '@angular/router'; -export type TInventoriesRouteNames = - | 'inventories' - | 'inventory' - | 'transfer' - | 'products' - | 'productCardex' - | 'posAccounts'; - -export const inventoriesNamedRoutes: NamedRoutes = { +export const inventoriesNamedRoutes: NamedRoutes = { inventories: { path: 'inventories', loadComponent: () => import('../../views/list.component').then((m) => m.InventoriesComponent), @@ -65,6 +57,17 @@ export const inventoriesNamedRoutes: NamedRoutes = { pagePath: () => '/inventories/:inventoryId/pos-accounts', }, }, -}; + purchase: { + path: 'inventories/:inventoryId/purchase', + loadComponent: () => + import('../../views/purchase.component').then((m) => m.InventoryPurchaseComponent), + meta: { + title: 'ثبت خرید در انبار', + pagePath: () => '/inventories/:inventoryId/purchase', + }, + }, +} as const; + +export type TInventoriesRouteNames = keyof typeof inventoriesNamedRoutes; export const INVENTORIES_ROUTES: Routes = Object.values(inventoriesNamedRoutes); diff --git a/src/app/modules/inventories/models/bankAccounts.io.ts b/src/app/modules/inventories/models/bankAccounts.io.ts index bd4b9e4..f1590c5 100644 --- a/src/app/modules/inventories/models/bankAccounts.io.ts +++ b/src/app/modules/inventories/models/bankAccounts.io.ts @@ -21,3 +21,24 @@ export interface IInventoryBankAccountRequest extends IBankAccountsRequest {} export interface IInventoryAssignBankAccountRequest { bankAccountId: number; } + +export interface IBankAccountSummary { + branch: Branch; + accountNumber: string; + cardNumber: string; + name: string; + id: number; + iban: string; +} + +interface Branch { + id: number; + name: string; + bank: Bank; +} + +interface Bank { + id: number; + name: string; + shortName: string; +} diff --git a/src/app/modules/inventories/store/inventory.store.ts b/src/app/modules/inventories/store/inventory.store.ts index 9cfc318..566a89a 100644 --- a/src/app/modules/inventories/store/inventory.store.ts +++ b/src/app/modules/inventories/store/inventory.store.ts @@ -43,10 +43,13 @@ export class InventoryStore extends EntityStore { + console.log('first'); + this.patchState({ entities: { [res.id]: res }, loading: false, }); + console.log(this.entities()); }, error: (err) => { this.patchState({ loading: false, error: err }); diff --git a/src/app/modules/inventories/views/purchase.component.html b/src/app/modules/inventories/views/purchase.component.html new file mode 100644 index 0000000..d7bdecc --- /dev/null +++ b/src/app/modules/inventories/views/purchase.component.html @@ -0,0 +1,4 @@ +@if (loading()) { +} @else { + +} diff --git a/src/app/modules/inventories/views/purchase.component.ts b/src/app/modules/inventories/views/purchase.component.ts new file mode 100644 index 0000000..8022e69 --- /dev/null +++ b/src/app/modules/inventories/views/purchase.component.ts @@ -0,0 +1,22 @@ +import { PurchaseReceiptTemplateComponent } from '@/modules/purchases/components'; +import { Component, computed, inject } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { InventoryStore } from '../store/inventory.store'; + +@Component({ + selector: 'app-inventory-purchase', + templateUrl: './purchase.component.html', + imports: [PurchaseReceiptTemplateComponent], +}) +export class InventoryPurchaseComponent { + private readonly route = inject(ActivatedRoute); + private readonly store = inject(InventoryStore); + + inventoryId = this.route.snapshot.paramMap.get('inventoryId')!; + + inventory = computed(() => this.store.entities()[this.inventoryId]); + loading = this.store.loading; + constructor() { + this.store.getSingle(this.inventoryId); + } +} diff --git a/src/app/modules/inventories/views/single.component.html b/src/app/modules/inventories/views/single.component.html index 7996a39..30f6895 100644 --- a/src/app/modules/inventories/views/single.component.html +++ b/src/app/modules/inventories/views/single.component.html @@ -14,14 +14,19 @@ icon="pi pi-plus" [routerLink]="['/inventories', inventoryId, 'pos-accounts']" > - - + diff --git a/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.html b/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.html index 8b15fc6..4a0597a 100644 --- a/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.html +++ b/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.html @@ -151,8 +151,8 @@
- مالیات (۹٪): - + مالیات (۱۰٪): +
@@ -161,12 +161,12 @@
مبلغ قابل پرداخت: - +
- +
diff --git a/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.ts b/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.ts index 427d4e7..ef567f4 100644 --- a/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.ts +++ b/src/app/modules/purchases/components/receiptTemplate/purchase-receipt-template.component.ts @@ -10,7 +10,7 @@ import { IColumn } from '@/shared/components/pageDataList/page-data-list.compone import { PriceAlphabetDirective, PriceMaskDirective } from '@/shared/directives'; import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { CommonModule } from '@angular/common'; -import { Component, inject, Input, signal } from '@angular/core'; +import { Component, effect, inject, Input, signal } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import dayjs from 'dayjs'; import { ButtonDirective } from 'primeng/button'; @@ -130,6 +130,15 @@ export class PurchaseReceiptTemplateComponent { this.form.controls.paidAmount.setValue(0); } }); + + effect(() => { + if (this.inventory) { + this.form.controls.inventory.setValue(this.inventory); + } + if (this.supplier) { + this.form.controls.supplier.setValue(this.supplier); + } + }); } formIsSubmitted = signal(false); diff --git a/src/app/modules/suppliers/components/invoices/payments.component.html b/src/app/modules/suppliers/components/invoices/payments.component.html new file mode 100644 index 0000000..1a2828d --- /dev/null +++ b/src/app/modules/suppliers/components/invoices/payments.component.html @@ -0,0 +1,5 @@ + + + + + diff --git a/src/app/modules/suppliers/components/invoices/payments.component.ts b/src/app/modules/suppliers/components/invoices/payments.component.ts new file mode 100644 index 0000000..ad77c52 --- /dev/null +++ b/src/app/modules/suppliers/components/invoices/payments.component.ts @@ -0,0 +1,75 @@ +import { CatalogPaymentMethodTypeTagComponent } from '@/shared/catalog/paymentMethodTypes'; +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, Input, signal, TemplateRef, ViewChild } from '@angular/core'; +import { IInvoicePaymentResponse } from '../../models/invoices.io'; +import { SupplierInvoicesService } from '../../services'; + +@Component({ + selector: 'app-invoice-payments', + templateUrl: './payments.component.html', + imports: [PageDataListComponent, CatalogPaymentMethodTypeTagComponent], +}) +export class InvoicePaymentsComponent { + @Input() supplierId!: string; + @Input() invoiceId!: string; + @ViewChild('paymentMethodTpl', { static: true }) paymentMethodTpl!: TemplateRef; + constructor(private readonly service: SupplierInvoicesService) {} + + columns = [] as IColumn[]; + + ngOnInit() { + console.log(this.invoiceId); + this.columns = [ + { + field: 'index', + header: 'ردیف', + type: 'index', + }, + { + field: 'code', + header: 'شناسه پرداخت', + }, + { + field: 'amount', + header: 'مبلغ پرداختی', + type: 'price', + }, + { + field: 'paymentMethod', + header: 'روش پرداخت', + customDataModel: this.paymentMethodTpl, + }, + { + field: 'bankAccount', + header: 'حساب بانکی', + customDataModel: (item: IInvoicePaymentResponse) => + `${item.bankAccount.name} - بانک ${item.bankAccount.branch.bank.name} - شعبه ${item.bankAccount.branch.name}`, + }, + { + field: 'payedAt', + header: 'تاریخ پرداخت', + type: 'date', + }, + ]; + this.getData(); + } + + items = signal([]); + loading = signal(false); + + getData() { + this.loading.set(true); + return this.service.getPayments(this.supplierId!, this.invoiceId!).subscribe({ + next: (res) => { + this.items.set(res.data); + this.loading.set(false); + }, + error: (err) => { + this.loading.set(false); + }, + }); + } +} diff --git a/src/app/modules/suppliers/components/ledger/ledger.component.html b/src/app/modules/suppliers/components/ledger/ledger.component.html index 33bdd30..101070d 100644 --- a/src/app/modules/suppliers/components/ledger/ledger.component.html +++ b/src/app/modules/suppliers/components/ledger/ledger.component.html @@ -4,7 +4,7 @@ دفتر کل تامین‌کننده
@if (showViewAllCTA) { - + }
@@ -33,7 +33,7 @@ - هیچ فاکتوری ثبت نشده است. + هیچ رکوردی ثبت نشده است. diff --git a/src/app/modules/suppliers/constants/apiRoutes/invoices.ts b/src/app/modules/suppliers/constants/apiRoutes/invoices.ts index a994181..29c52e8 100644 --- a/src/app/modules/suppliers/constants/apiRoutes/invoices.ts +++ b/src/app/modules/suppliers/constants/apiRoutes/invoices.ts @@ -4,5 +4,7 @@ export const SUPPLIER_INVOICES_API_ROUTES = (_baseUrl: string) => { list: (supplierId: string) => `${baseUrl(supplierId)}`, single: (supplierId: string, invoiceId: string) => `${baseUrl(supplierId)}/${invoiceId}`, pay: (supplierId: string, invoiceId: string) => `${baseUrl(supplierId)}/${invoiceId}/pay`, + invoicePayments: (supplierId: string, invoiceId: string) => + `${baseUrl(supplierId)}/${invoiceId}/payments`, }; }; diff --git a/src/app/modules/suppliers/models/invoices.io.ts b/src/app/modules/suppliers/models/invoices.io.ts index 93a00a8..023bcaf 100644 --- a/src/app/modules/suppliers/models/invoices.io.ts +++ b/src/app/modules/suppliers/models/invoices.io.ts @@ -1,8 +1,9 @@ import ISummary from '@/core/models/summary'; +import { IBankAccountSummary } from '@/modules/inventories/models'; import { PurchaseReceiptStatus } from '@/shared/catalog'; import { PaymentMethodType } from '@/shared/catalog/paymentMethodTypes'; import { PaymentType } from '@/shared/catalog/paymentTypes'; -import { ISupplierReceiptItemSummary } from './types'; +import { IReceiptInfo, ISupplierReceiptItemSummary } from './types'; export interface ISupplierInvoicesRawResponse { id: number; @@ -37,3 +38,18 @@ export interface IPayRawResponse { description?: string; } export interface IPayResponse extends IPayRawResponse {} + +export interface IInvoicePaymentRawResponse { + id: number; + amount: string; + paymentMethod: string; + type: string; + bankAccountId: number; + payedAt: string; + description: null; + createdAt: string; + receipt: IReceiptInfo; + bankAccount: IBankAccountSummary; +} + +export interface IInvoicePaymentResponse extends IInvoicePaymentRawResponse {} diff --git a/src/app/modules/suppliers/models/types.ts b/src/app/modules/suppliers/models/types.ts index 8a50f77..70383e9 100644 --- a/src/app/modules/suppliers/models/types.ts +++ b/src/app/modules/suppliers/models/types.ts @@ -1,3 +1,5 @@ +import ISummary from '@/core/models/summary'; + export interface IReceiptsOverview { totalPayment: string; count: number; @@ -19,3 +21,17 @@ export interface ISupplierReceiptItemSummary { sku: string; }; } + +export interface IReceiptInfo { + id: number; + code: string; + totalAmount: string; + paidAmount: string; + description: string; + createdAt: string; + updatedAt: string; + status: string; + supplierId: number; + inventoryId: number; + inventory: ISummary; +} diff --git a/src/app/modules/suppliers/services/invoices.service.ts b/src/app/modules/suppliers/services/invoices.service.ts index 5aa8673..55410d2 100644 --- a/src/app/modules/suppliers/services/invoices.service.ts +++ b/src/app/modules/suppliers/services/invoices.service.ts @@ -4,6 +4,8 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { SUPPLIERS_API_ROUTES } from '../constants'; import { + IInvoicePaymentRawResponse, + IInvoicePaymentResponse, IPayRawResponse, IPayRequestPayload, IPayResponse, @@ -36,4 +38,13 @@ export class SupplierInvoicesService { ): Observable { return this.http.post(this.apiRoutes.pay(supplierId, invoiceId), payload); } + + getPayments( + supplierId: string, + invoiceId: string, + ): Observable> { + return this.http.get>( + this.apiRoutes.invoicePayments(supplierId, invoiceId), + ); + } } diff --git a/src/app/modules/suppliers/views/invoice.component.html b/src/app/modules/suppliers/views/invoice.component.html index 7d3e4c3..ebba74a 100644 --- a/src/app/modules/suppliers/views/invoice.component.html +++ b/src/app/modules/suppliers/views/invoice.component.html @@ -21,6 +21,7 @@ + @if (invoice() && supplier()) {
-
- -
+ @if (supplier()) { +
+ +
+ }
@for (item of topBarCardDetails; track $index) { - + }
diff --git a/src/app/shared/components/input/input.component.ts b/src/app/shared/components/input/input.component.ts index 3d0e50c..e3d4de9 100644 --- a/src/app/shared/components/input/input.component.ts +++ b/src/app/shared/components/input/input.component.ts @@ -1,5 +1,4 @@ import { Maybe } from '@/core'; -import { PriceMaskDirective } from '@/shared/directives'; import { UikitFieldComponent } from '@/uikit/uikit-field.component'; import { CommonModule } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; @@ -20,7 +19,6 @@ import { ToggleSwitch } from 'primeng/toggleswitch'; InputGroupModule, UikitFieldComponent, ToggleSwitch, - PriceMaskDirective, InputGroupAddon, InputNumberModule, ], diff --git a/src/app/shared/components/pageDataList/page-data-list.component.html b/src/app/shared/components/pageDataList/page-data-list.component.html index c78e438..0ebd3b9 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.html +++ b/src/app/shared/components/pageDataList/page-data-list.component.html @@ -41,7 +41,15 @@ @for (col of columns; track col.header) { - {{ col.header }} + + {{ col.header }} + } @if (actionsCount()) { @@ -49,11 +57,13 @@ - + @for (col of columns; track col.field) { - @if (getTemplate(col)) { + @if (col.type === "index") { + {{ i * (currentPage || 1) + 1 }} + } @else if (getTemplate(col)) { { width?: string; minWidth?: string; canCopy?: boolean; - type?: 'text' | 'price' | 'boolean' | 'date' | 'nested'; + type?: 'text' | 'price' | 'boolean' | 'date' | 'nested' | 'index'; nestedPath?: string; customDataModel?: TemplateRef | ((item: T) => string | number | boolean); }