From abfb2f347980d3581dcf1eb938b713b2c47723ad Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Fri, 26 Dec 2025 01:20:44 +0330 Subject: [PATCH] create bankAccount, posAccount and update inventories --- angular.json | 4 +- .../auth/pages/login/login.component.ts | 2 - .../components/form-template.component.html | 9 ++ .../components/form-template.component.ts | 59 +++++++ .../components/form.component.html | 10 +- .../bankAccounts/components/form.component.ts | 84 +++------- .../components/select/select.component.html | 2 +- .../components/select/select.component.html | 2 +- src/app/modules/cardex/store/main.store.ts | 1 - .../create-bank-account-form.component.html | 10 ++ .../create-bank-account-form.component.ts | 57 +++++++ .../bankAccounts/form.component.html | 6 + .../components/bankAccounts/form.component.ts | 37 +++++ .../bankAccounts/list.component.html | 33 ++++ .../components/bankAccounts/list.component.ts | 152 ++++++++++++++++++ .../bankAccounts/select.component.html | 30 ++++ .../bankAccounts/select.component.ts | 45 ++++++ .../movementList/movement-list.component.html | 9 +- .../posAccounts/form.component.html | 15 ++ .../components/posAccounts/form.component.ts | 51 ++++++ .../transfer/transfer-form.component.ts | 4 - .../inventories/constants/apiRoutes/index.ts | 6 + .../inventories/constants/apiRoutes/pos.ts | 8 + .../inventories/constants/routes/index.ts | 14 +- .../inventories/models/bankAccounts.io.ts | 23 +++ src/app/modules/inventories/models/index.ts | 2 + .../inventories/models/posAccounts.io.ts | 23 +++ src/app/modules/inventories/models/types.ts | 42 +++++ .../services/bankAccounts.service.ts | 55 +++++++ src/app/modules/inventories/services/index.ts | 2 + .../services/posAccounts.service.ts | 33 ++++ src/app/modules/inventories/views/index.ts | 1 + .../inventories/views/posAccounts/index.ts | 1 + .../views/posAccounts/list.component.html | 23 +++ .../views/posAccounts/list.component.ts | 76 +++++++++ .../inventories/views/single.component.html | 9 ++ .../inventories/views/single.component.ts | 2 + src/app/modules/pos/store/index.ts | 2 - .../components/select/select.component.ts | 2 - .../abstract-form-dialog.component.ts | 67 ++++++++ .../components/abstract-select.component.ts | 1 + .../pageDataList/page-data-list.component.ts | 1 + 42 files changed, 920 insertions(+), 95 deletions(-) create mode 100644 src/app/modules/bankAccounts/components/form-template.component.html create mode 100644 src/app/modules/bankAccounts/components/form-template.component.ts create mode 100644 src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.html create mode 100644 src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.ts create mode 100644 src/app/modules/inventories/components/bankAccounts/form.component.html create mode 100644 src/app/modules/inventories/components/bankAccounts/form.component.ts create mode 100644 src/app/modules/inventories/components/bankAccounts/list.component.html create mode 100644 src/app/modules/inventories/components/bankAccounts/list.component.ts create mode 100644 src/app/modules/inventories/components/bankAccounts/select.component.html create mode 100644 src/app/modules/inventories/components/bankAccounts/select.component.ts create mode 100644 src/app/modules/inventories/components/posAccounts/form.component.html create mode 100644 src/app/modules/inventories/components/posAccounts/form.component.ts create mode 100644 src/app/modules/inventories/constants/apiRoutes/pos.ts create mode 100644 src/app/modules/inventories/models/bankAccounts.io.ts create mode 100644 src/app/modules/inventories/models/posAccounts.io.ts create mode 100644 src/app/modules/inventories/services/bankAccounts.service.ts create mode 100644 src/app/modules/inventories/services/index.ts create mode 100644 src/app/modules/inventories/services/posAccounts.service.ts create mode 100644 src/app/modules/inventories/views/posAccounts/index.ts create mode 100644 src/app/modules/inventories/views/posAccounts/list.component.html create mode 100644 src/app/modules/inventories/views/posAccounts/list.component.ts create mode 100644 src/app/shared/components/abstract-form-dialog.component.ts diff --git a/angular.json b/angular.json index 879e8a8..57dc6e7 100644 --- a/angular.json +++ b/angular.json @@ -18,8 +18,8 @@ "production": { "budgets": [ { - "maximumError": "1MB", - "maximumWarning": "500kB", + "maximumError": "3MB", + "maximumWarning": "2MB", "type": "initial" }, { diff --git a/src/app/modules/auth/pages/login/login.component.ts b/src/app/modules/auth/pages/login/login.component.ts index 5e39f2d..bd67bcd 100644 --- a/src/app/modules/auth/pages/login/login.component.ts +++ b/src/app/modules/auth/pages/login/login.component.ts @@ -1,7 +1,6 @@ import { AuthService } from '@/core'; import { IAuthResponse, LoginCredentials, TRoles } from '@/core/models'; import { ToastService } from '@/core/services/toast.service'; -import { UikitLabelComponent } from '@/uikit'; import { UikitFieldComponent } from '@/uikit/uikit-field.component'; import { CommonModule } from '@angular/common'; import { Component, computed, EventEmitter, inject, Input, Output, signal } from '@angular/core'; @@ -29,7 +28,6 @@ import { CaptchaService } from 'src/app/core/services/captcha.service'; InputText, ImageModule, ProgressSpinnerModule, - UikitLabelComponent, RadioButtonModule, InputGroupModule, InputGroupAddonModule, diff --git a/src/app/modules/bankAccounts/components/form-template.component.html b/src/app/modules/bankAccounts/components/form-template.component.html new file mode 100644 index 0000000..cd9f5ad --- /dev/null +++ b/src/app/modules/bankAccounts/components/form-template.component.html @@ -0,0 +1,9 @@ +
+ + + + + + + + diff --git a/src/app/modules/bankAccounts/components/form-template.component.ts b/src/app/modules/bankAccounts/components/form-template.component.ts new file mode 100644 index 0000000..1c2abd6 --- /dev/null +++ b/src/app/modules/bankAccounts/components/form-template.component.ts @@ -0,0 +1,59 @@ +import { BankBranchesSelectComponent } from '@/modules/bankBranches/components/select/select.component'; +import { InputComponent } from '@/shared/components'; +import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { Component, EventEmitter, inject, Input, Output } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { IBankAccountsRequest, IBankAccountsResponse } from '../models'; + +@Component({ + selector: 'bank-account-form-template', + templateUrl: './form-template.component.html', + imports: [ + ReactiveFormsModule, + InputComponent, + FormFooterActionsComponent, + BankBranchesSelectComponent, + ], +}) +export class BankAccountFormTemplateComponent { + @Input() initialValues?: IBankAccountsResponse; + + @Output() onSubmitForm = new EventEmitter(); + @Output() onCancel = new EventEmitter(); + + fb = inject(FormBuilder); + + form = this.fb.group({ + name: this.fb.control(this.initialValues?.name || '', { + nonNullable: true, + validators: Validators.required, + }), + branchId: this.fb.control(this.initialValues?.branchId || 0, { + nonNullable: true, + validators: Validators.required, + }), + cardNumber: this.fb.control(this.initialValues?.cardNumber || '', { + nonNullable: true, + validators: Validators.required, + }), + accountNumber: this.fb.control(this.initialValues?.accountNumber || '', { + nonNullable: true, + validators: Validators.required, + }), + iban: this.fb.control(this.initialValues?.iban || '', { + nonNullable: true, + validators: Validators.required, + }), + }); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.onSubmitForm.emit(this.form.value as IBankAccountsRequest); + } + } + + close() { + this.onCancel.emit(); + } +} diff --git a/src/app/modules/bankAccounts/components/form.component.html b/src/app/modules/bankAccounts/components/form.component.html index 208367e..5b4880e 100644 --- a/src/app/modules/bankAccounts/components/form.component.html +++ b/src/app/modules/bankAccounts/components/form.component.html @@ -6,13 +6,5 @@ appendTo="body" [closable]="true" > -
- - - - - - - - + diff --git a/src/app/modules/bankAccounts/components/form.component.ts b/src/app/modules/bankAccounts/components/form.component.ts index 59e0068..dee71b8 100644 --- a/src/app/modules/bankAccounts/components/form.component.ts +++ b/src/app/modules/bankAccounts/components/form.component.ts @@ -1,25 +1,15 @@ import { ToastService } from '@/core/services/toast.service'; -import { BankBranchesSelectComponent } from '@/modules/bankBranches/components/select/select.component'; -import { BanksSelectComponent } from '@/shared/catalog/banks'; -import { InputComponent } from '@/shared/components'; -import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; -import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; -import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Component, EventEmitter, Input, Output, signal } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; import { Dialog } from 'primeng/dialog'; import { IBankAccountsRequest, IBankAccountsResponse } from '../models'; import { BankAccountsService } from '../services/main.service'; +import { BankAccountFormTemplateComponent } from './form-template.component'; @Component({ selector: 'bank-account-form', templateUrl: './form.component.html', - imports: [ - ReactiveFormsModule, - Dialog, - InputComponent, - FormFooterActionsComponent, - BanksSelectComponent, - BankBranchesSelectComponent, - ], + imports: [ReactiveFormsModule, Dialog, BankAccountFormTemplateComponent], }) export class BankAccountFormComponent { @Input() initialValues?: IBankAccountsResponse; @@ -36,64 +26,26 @@ export class BankAccountFormComponent { @Output() visibleChange = new EventEmitter(); @Output() onSubmit = new EventEmitter(); - private fb = inject(FormBuilder); constructor( private service: BankAccountsService, private toastService: ToastService, - ) { - effect(() => { - const v = this.visibleSignal(); - if (!v) this.form.reset(); - }); - } - - form = this.fb.group({ - name: this.fb.control(this.initialValues?.name || '', { - nonNullable: true, - validators: Validators.required, - }), - branchId: this.fb.control(this.initialValues?.branchId || 0, { - nonNullable: true, - validators: Validators.required, - }), - cardNumber: this.fb.control(this.initialValues?.cardNumber || '', { - nonNullable: true, - validators: Validators.required, - }), - accountNumber: this.fb.control(this.initialValues?.accountNumber || '', { - nonNullable: true, - validators: Validators.required, - }), - iban: this.fb.control(this.initialValues?.iban || '', { - nonNullable: true, - validators: Validators.required, - }), - }); + ) {} submitLoading = signal(false); - submit() { - this.form.markAllAsTouched(); - if (this.form.valid) { - this.form.disable(); - this.submitLoading.set(true); - this.service.create(this.form.value as IBankAccountsRequest).subscribe({ - next: (res) => { - this.toastService.success({ - text: `حساب بانکی ${this.form.value.name} با موفقیت ایجاد شد`, - }); - this.close(); - this.submitLoading.set(false); - this.form.enable(); - this.form.reset(); - this.onSubmit.emit(res); - }, - error: () => { - this.submitLoading.set(false); - this.form.enable(); - }, - }); - } + submit(payload: IBankAccountsRequest) { + this.submitLoading.set(true); + this.service.create(payload).subscribe({ + next: (res) => { + this.toastService.success({ + text: `حساب بانکی ${payload.name} با موفقیت ایجاد شد`, + }); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + }, + }); } close() { diff --git a/src/app/modules/bankAccounts/components/select/select.component.html b/src/app/modules/bankAccounts/components/select/select.component.html index d6bc026..d8c3dc2 100644 --- a/src/app/modules/bankAccounts/components/select/select.component.html +++ b/src/app/modules/bankAccounts/components/select/select.component.html @@ -6,7 +6,7 @@ [optionValue]="selectOptionValue" placeholder="انتخاب حساب بانکی" [formControl]="control" - [showClear]="true" + [showClear]="showClear" [filter]="true" appendTo="body" > diff --git a/src/app/modules/bankBranches/components/select/select.component.html b/src/app/modules/bankBranches/components/select/select.component.html index f327d6e..0c2193e 100644 --- a/src/app/modules/bankBranches/components/select/select.component.html +++ b/src/app/modules/bankBranches/components/select/select.component.html @@ -6,7 +6,7 @@ [optionValue]="selectOptionValue" placeholder="انتخاب شعبه‌ی بانک" [formControl]="control" - [showClear]="true" + [showClear]="showClear" [filter]="true" appendTo="body" > diff --git a/src/app/modules/cardex/store/main.store.ts b/src/app/modules/cardex/store/main.store.ts index 728bd1a..f633125 100644 --- a/src/app/modules/cardex/store/main.store.ts +++ b/src/app/modules/cardex/store/main.store.ts @@ -150,7 +150,6 @@ export class CardexStore extends PaginatedStore { updateFilter(filters: ICardexRequestPayload) { this.updateVariant(); - console.log(filters); this.router.navigate([], { queryParams: filters, diff --git a/src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.html b/src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.html new file mode 100644 index 0000000..5b4880e --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.html @@ -0,0 +1,10 @@ + + + diff --git a/src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.ts b/src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.ts new file mode 100644 index 0000000..83c26ee --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/create-bank-account-form.component.ts @@ -0,0 +1,57 @@ +import { ToastService } from '@/core/services/toast.service'; +import { BankAccountFormTemplateComponent } from '@/modules/bankAccounts/components/form-template.component'; +import { IBankAccountsResponse } from '@/modules/bankAccounts/models'; +import { Component, EventEmitter, Input, Output, signal } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { IInventoryBankAccountRequest } from '../../models/bankAccounts.io'; +import { InventoryBankAccountsService } from '../../services'; + +@Component({ + selector: 'inventory-create-bank-account-form', + templateUrl: './create-bank-account-form.component.html', + imports: [ReactiveFormsModule, Dialog, BankAccountFormTemplateComponent], +}) +export class InventoryCreateBankAccountFormComponent { + @Input() inventoryId!: number; + @Input() initialValues?: IBankAccountsResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + constructor( + private service: InventoryBankAccountsService, + private toastService: ToastService, + ) {} + + submitLoading = signal(false); + + submit(payload: IInventoryBankAccountRequest) { + this.submitLoading.set(true); + this.service.create(this.inventoryId, payload).subscribe({ + next: (res) => { + this.toastService.success({ + text: `حساب بانکی ${payload.name} با موفقیت ایجاد شد`, + }); + this.close(); + this.onSubmit.emit(); + }, + error: () => { + this.submitLoading.set(false); + }, + }); + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/inventories/components/bankAccounts/form.component.html b/src/app/modules/inventories/components/bankAccounts/form.component.html new file mode 100644 index 0000000..dc5c37a --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/form.component.html @@ -0,0 +1,6 @@ + +
+ + + +
diff --git a/src/app/modules/inventories/components/bankAccounts/form.component.ts b/src/app/modules/inventories/components/bankAccounts/form.component.ts new file mode 100644 index 0000000..e6cfb37 --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/form.component.ts @@ -0,0 +1,37 @@ +import { BankAccountsSelectComponent } from '@/modules/bankAccounts/components/select/select.component'; +import { AbstractFormDialog } from '@/shared/components/abstract-form-dialog.component'; +import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { Component, inject } from '@angular/core'; +import { ReactiveFormsModule, Validators } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { Dialog } from 'primeng/dialog'; +import { + IInventoryAssignBankAccountRequest, + IInventoryBankAccountResponse, +} from '../../models/bankAccounts.io'; +import { InventoryBankAccountsService } from '../../services'; + +@Component({ + selector: 'app-inventory-bank-account-form', + templateUrl: './form.component.html', + imports: [ReactiveFormsModule, Dialog, BankAccountsSelectComponent, FormFooterActionsComponent], +}) +export class InventoryBankAccountFormComponent extends AbstractFormDialog< + IInventoryBankAccountResponse, + IInventoryAssignBankAccountRequest +> { + private readonly route = inject(ActivatedRoute); + private readonly service = inject(InventoryBankAccountsService); + + form = this.fb.group({ + bankAccountId: this.fb.control(0, { + nonNullable: true, + validators: [Validators.required], + }), + }); + + submitForm(payload: IInventoryAssignBankAccountRequest) { + const inventoryId = this.route.snapshot.paramMap.get('inventoryId')!; + return this.service.assign(Number(inventoryId), payload); + } +} diff --git a/src/app/modules/inventories/components/bankAccounts/list.component.html b/src/app/modules/inventories/components/bankAccounts/list.component.html new file mode 100644 index 0000000..f014a67 --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/list.component.html @@ -0,0 +1,33 @@ + + +
+ حساب‌های بانکی متصل به انبار +
+ + + +
+
+
+ + + + + + + + + +
+ diff --git a/src/app/modules/inventories/components/bankAccounts/list.component.ts b/src/app/modules/inventories/components/bankAccounts/list.component.ts new file mode 100644 index 0000000..36c4d28 --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/list.component.ts @@ -0,0 +1,152 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, inject, signal, TemplateRef, ViewChild } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { ButtonDirective } from 'primeng/button'; +import { Card } from 'primeng/card'; +import { ConfirmDialog } from 'primeng/confirmdialog'; +import { InventoryPosAccountFormComponent } from '../../components/posAccounts/form.component'; +import { IInventoryBankAccountResponse } from '../../models/bankAccounts.io'; +import { InventoryBankAccountsService } from '../../services'; +import { InventoryCreateBankAccountFormComponent } from './create-bank-account-form.component'; +import { InventoryBankAccountFormComponent } from './form.component'; + +@Component({ + selector: 'app-inventory-bank-accounts-list', + templateUrl: './list.component.html', + imports: [ + PageDataListComponent, + Card, + ButtonDirective, + ConfirmDialog, + InventoryPosAccountFormComponent, + InventoryBankAccountFormComponent, + InventoryCreateBankAccountFormComponent, + ], + providers: [ConfirmationService], +}) +export class InventoryBankAccountsListComponent { + private confirmationService = inject(ConfirmationService); + private messageService = inject(MessageService); + private readonly route = inject(ActivatedRoute); + private readonly service = inject(InventoryBankAccountsService); + @ViewChild('actions', { static: true }) actionsTpl!: TemplateRef; + + items = signal([]); + loading = signal(false); + visibleForm = signal(false); + visibleBankAccountForm = signal(false); + inventoryId = this.route.snapshot.params['inventoryId']; + + columns = [] as IColumn[]; + + ngOnInit() { + this.getItems(); + this.columns = [ + { + field: 'name', + header: 'عنوان حساب', + customDataModel(item) { + return item.name; + }, + }, + { + field: 'bankName', + header: 'نام بانک', + customDataModel(item) { + return `${item.branch.bank.name} - شعبه‌ی ${item.name}`; + }, + }, + { + field: 'accountNumber', + header: 'شماره حساب', + customDataModel(item) { + return item.accountNumber; + }, + }, + { + field: 'relatedPOSes', + header: 'نقاط فروش متصل', + customDataModel(item) { + return item.posAccounts.length || '0'; + }, + }, + { + field: 'actions', + header: '', + customDataModel: this.actionsTpl, + width: '100px', + }, + ]; + } + + getItems() { + this.loading.set(true); + this.service.getBankAccounts(this.inventoryId).subscribe({ + next: (res) => { + this.items.set(res.data); + this.loading.set(false); + }, + error: () => { + this.loading.set(false); + }, + }); + } + + refresh() { + this.getItems(); + } + + openAddForm() { + this.visibleForm.set(true); + } + openCreateForm() { + this.visibleBankAccountForm.set(true); + } + + showConfirmation(event: MouseEvent, item: IInventoryBankAccountResponse) { + this.confirmationService.confirm({ + target: event.target as EventTarget, + message: 'از حذف این حساب بانکی اطمینان دارید؟', + header: 'تأییدیه', + closable: true, + closeOnEscape: true, + icon: 'pi pi-exclamation-triangle', + rejectButtonProps: { + label: 'لغو', + severity: 'secondary', + outlined: true, + }, + acceptButtonProps: { + label: 'تایید', + }, + accept: () => { + this.doUnassignBankAccount(item); + }, + }); + } + + unassignBankAccount(event: MouseEvent, item: IInventoryBankAccountResponse) { + this.showConfirmation(event, item); + } + doUnassignBankAccount(item: IInventoryBankAccountResponse) { + this.loading.set(true); + this.service.unassign(this.inventoryId, { bankAccountId: item.id }).subscribe({ + next: () => { + this.messageService.add({ + severity: 'success', + summary: 'موفق', + detail: 'حساب بانکی با موفقیت حذف شد', + }); + this.loading.set(false); + this.getItems(); + }, + error: () => { + this.loading.set(false); + }, + }); + } +} diff --git a/src/app/modules/inventories/components/bankAccounts/select.component.html b/src/app/modules/inventories/components/bankAccounts/select.component.html new file mode 100644 index 0000000..14b7e1b --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/select.component.html @@ -0,0 +1,30 @@ + + + @if (canInsert) { + +
+ +
+
+ } +
+ +
diff --git a/src/app/modules/inventories/components/bankAccounts/select.component.ts b/src/app/modules/inventories/components/bankAccounts/select.component.ts new file mode 100644 index 0000000..d8fee77 --- /dev/null +++ b/src/app/modules/inventories/components/bankAccounts/select.component.ts @@ -0,0 +1,45 @@ +import { AbstractSelectComponent } from '@/shared/components'; +import { UikitFieldComponent } from '@/uikit'; +import { Component, Input } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { Button } from 'primeng/button'; +import { Select } from 'primeng/select'; +import { IInventoryBankAccountResponse } from '../../models'; +import { InventoryBankAccountsService } from '../../services'; +import { InventoryBankAccountFormComponent } from './form.component'; + +@Component({ + selector: 'app-inventory-bank-account-select', + templateUrl: './select.component.html', + imports: [ + UikitFieldComponent, + Select, + Button, + InventoryBankAccountFormComponent, + ReactiveFormsModule, + ], +}) +export class InventoryBankAccountSelectComponent extends AbstractSelectComponent { + @Input() inventoryId!: string; + @Input() override showClear: boolean = false; + constructor(private service: InventoryBankAccountsService) { + super(); + } + + ngOnInit() { + this.getData(); + } + + getData(): void { + this.loading.set(true); + this.service.getBankAccounts(Number(this.inventoryId)).subscribe({ + next: (res) => { + this.loading.set(false); + this.items.set(res.data); + }, + error: () => { + this.loading.set(false); + }, + }); + } +} diff --git a/src/app/modules/inventories/components/movementList/movement-list.component.html b/src/app/modules/inventories/components/movementList/movement-list.component.html index c2d2f63..ab812c9 100644 --- a/src/app/modules/inventories/components/movementList/movement-list.component.html +++ b/src/app/modules/inventories/components/movementList/movement-list.component.html @@ -33,7 +33,11 @@ {{ movement.receiptId }} - + {{ movement.count }} {{ movement.info.quantity }} @@ -92,9 +96,6 @@ {{ movement.quantity }} - - - diff --git a/src/app/modules/inventories/components/posAccounts/form.component.html b/src/app/modules/inventories/components/posAccounts/form.component.html new file mode 100644 index 0000000..82c9966 --- /dev/null +++ b/src/app/modules/inventories/components/posAccounts/form.component.html @@ -0,0 +1,15 @@ + +
+ + + + + +
diff --git a/src/app/modules/inventories/components/posAccounts/form.component.ts b/src/app/modules/inventories/components/posAccounts/form.component.ts new file mode 100644 index 0000000..bb0a850 --- /dev/null +++ b/src/app/modules/inventories/components/posAccounts/form.component.ts @@ -0,0 +1,51 @@ +import { InputComponent } from '@/shared/components'; +import { AbstractFormDialog } from '@/shared/components/abstract-form-dialog.component'; +import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { Component, inject, Input } from '@angular/core'; +import { ReactiveFormsModule, Validators } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { IInventoryPosAccountRequest, IInventoryPosAccountResponse } from '../../models'; +import { InventoryPosAccountsService } from '../../services/posAccounts.service'; +import { InventoryBankAccountSelectComponent } from '../bankAccounts/select.component'; + +@Component({ + selector: 'app-inventory-pos-account-form', + templateUrl: './form.component.html', + imports: [ + Dialog, + ReactiveFormsModule, + InputComponent, + FormFooterActionsComponent, + InventoryBankAccountSelectComponent, + ], +}) +export class InventoryPosAccountFormComponent extends AbstractFormDialog< + IInventoryPosAccountResponse, + IInventoryPosAccountRequest +> { + @Input() inventoryId!: string; + + private readonly service = inject(InventoryPosAccountsService); + + form = this.fb.group({ + name: this.fb.control(this.initialValues?.name || '', { + nonNullable: true, + validators: [Validators.required], + }), + code: this.fb.control(this.initialValues?.code || '', { + nonNullable: true, + validators: [Validators.required], + }), + description: this.fb.control(this.initialValues?.description || '', { + nonNullable: true, + }), + bankAccountId: this.fb.control(this.initialValues?.bankAccount?.id || 0, { + nonNullable: true, + validators: [Validators.required], + }), + }); + + submitForm(payload: IInventoryPosAccountRequest) { + return this.service.create(Number(this.inventoryId), payload); + } +} diff --git a/src/app/modules/inventories/components/transfer/transfer-form.component.ts b/src/app/modules/inventories/components/transfer/transfer-form.component.ts index 4a89393..960e692 100644 --- a/src/app/modules/inventories/components/transfer/transfer-form.component.ts +++ b/src/app/modules/inventories/components/transfer/transfer-form.component.ts @@ -5,7 +5,6 @@ import { Component, inject, signal } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { ButtonDirective } from 'primeng/button'; import { Card } from 'primeng/card'; -import { InputText } from 'primeng/inputtext'; import { Select } from 'primeng/select'; import { TableModule, TableRowSelectEvent, TableRowUnSelectEvent } from 'primeng/table'; import { Textarea } from 'primeng/textarea'; @@ -16,7 +15,6 @@ import { IInventoryTransferRequest, } from '../../models'; import { InventoriesService } from '../../services/main.service'; -import { InventoriesSelectComponent } from '../select/select.component'; @Component({ selector: 'inventories-transfer-form', @@ -24,13 +22,11 @@ import { InventoriesSelectComponent } from '../select/select.component'; imports: [ ReactiveFormsModule, Card, - InventoriesSelectComponent, InputComponent, Textarea, UikitFieldComponent, Select, TableModule, - InputText, ButtonDirective, UikitCounterComponent, ], diff --git a/src/app/modules/inventories/constants/apiRoutes/index.ts b/src/app/modules/inventories/constants/apiRoutes/index.ts index 39390eb..fa153f4 100644 --- a/src/app/modules/inventories/constants/apiRoutes/index.ts +++ b/src/app/modules/inventories/constants/apiRoutes/index.ts @@ -1,3 +1,5 @@ +import { INVENTORY_POS_API_ROUTES } from './pos'; + const baseUrl = '/api/v1/inventories'; export const INVENTORIES_API_ROUTES = { @@ -8,4 +10,8 @@ export const INVENTORIES_API_ROUTES = { stock: (inventoryId: string) => `${baseUrl}/${inventoryId}/stock`, productCardex: (inventoryId: string, productId: string) => `${baseUrl}/${inventoryId}/products/${productId}/cardex`, + bankAccounts: (inventoryId: number) => `${baseUrl}/${inventoryId}/bank-accounts`, + assignBankAccounts: (inventoryId: number) => `${baseUrl}/${inventoryId}/bank-accounts/assign`, + unassignBankAccounts: (inventoryId: number) => `${baseUrl}/${inventoryId}/bank-accounts/unassign`, + pos: INVENTORY_POS_API_ROUTES(baseUrl), }; diff --git a/src/app/modules/inventories/constants/apiRoutes/pos.ts b/src/app/modules/inventories/constants/apiRoutes/pos.ts new file mode 100644 index 0000000..9143017 --- /dev/null +++ b/src/app/modules/inventories/constants/apiRoutes/pos.ts @@ -0,0 +1,8 @@ +export const INVENTORY_POS_API_ROUTES = (_baseUrl: string) => { + const baseUrl = (inventoryId: string) => `${_baseUrl}/${inventoryId}/pos-accounts`; + return { + list: (inventoryId: string) => `${baseUrl(inventoryId)}`, + single: (inventoryId: string, posAccountId: string) => + `${baseUrl(inventoryId)}/${posAccountId}`, + }; +}; diff --git a/src/app/modules/inventories/constants/routes/index.ts b/src/app/modules/inventories/constants/routes/index.ts index a88289c..87adf7c 100644 --- a/src/app/modules/inventories/constants/routes/index.ts +++ b/src/app/modules/inventories/constants/routes/index.ts @@ -6,7 +6,8 @@ export type TInventoriesRouteNames = | 'inventory' | 'transfer' | 'products' - | 'productCardex'; + | 'productCardex' + | 'posAccounts'; export const inventoriesNamedRoutes: NamedRoutes = { inventories: { @@ -53,6 +54,17 @@ export const inventoriesNamedRoutes: NamedRoutes = { pagePath: () => '/inventories/:inventoryId/products/:productId/cardex', }, }, + posAccounts: { + path: 'inventories/:inventoryId/pos-accounts', + loadComponent: () => + import('../../views/posAccounts/list.component').then( + (m) => m.InventoryPosAccountListComponent, + ), + meta: { + title: 'نقاط فروش متصل به انبار', + pagePath: () => '/inventories/:inventoryId/pos-accounts', + }, + }, }; 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 new file mode 100644 index 0000000..bd4b9e4 --- /dev/null +++ b/src/app/modules/inventories/models/bankAccounts.io.ts @@ -0,0 +1,23 @@ +import { IBankAccountsRequest } from '@/modules/bankAccounts/models'; +import { IBankAccountBranch, IInventoryPosAccountItemSummary } from './types'; + +export interface IInventoryBankAccountRawResponse { + id: number; + accountNumber: string; + cardNumber: null; + name: string; + iban: string; + branchId: number; + createdAt: string; + updatedAt: string; + deletedAt: null; + branch: IBankAccountBranch; + posAccounts: IInventoryPosAccountItemSummary[]; +} + +export interface IInventoryBankAccountResponse extends IInventoryBankAccountRawResponse {} + +export interface IInventoryBankAccountRequest extends IBankAccountsRequest {} +export interface IInventoryAssignBankAccountRequest { + bankAccountId: number; +} diff --git a/src/app/modules/inventories/models/index.ts b/src/app/modules/inventories/models/index.ts index d390b4d..fca944e 100644 --- a/src/app/modules/inventories/models/index.ts +++ b/src/app/modules/inventories/models/index.ts @@ -1,2 +1,4 @@ +export * from './bankAccounts.io'; export * from './io'; +export * from './posAccounts.io'; export * from './types'; diff --git a/src/app/modules/inventories/models/posAccounts.io.ts b/src/app/modules/inventories/models/posAccounts.io.ts new file mode 100644 index 0000000..4105a15 --- /dev/null +++ b/src/app/modules/inventories/models/posAccounts.io.ts @@ -0,0 +1,23 @@ +import { IInventoryBankAccountSummary } from './types'; + +export interface IInventoryPosAccountRawResponse { + id: number; + name: string; + code: string; + description: string; + bankAccountId: number; + inventoryId: number; + createdAt: string; + updatedAt: string; + deletedAt: null; + bankAccount: IInventoryBankAccountSummary; +} + +export interface IInventoryPosAccountResponse extends IInventoryPosAccountRawResponse {} + +export interface IInventoryPosAccountRequest { + name: string; + code: string; + description: string; + bankAccountId: number; +} diff --git a/src/app/modules/inventories/models/types.ts b/src/app/modules/inventories/models/types.ts index 2093824..8e2f3e3 100644 --- a/src/app/modules/inventories/models/types.ts +++ b/src/app/modules/inventories/models/types.ts @@ -40,3 +40,45 @@ export interface IInventoryTransferRequestItem { } export interface IInventoryStockProduct extends IProductRawResponse {} + +export interface IBankAccountBranch { + id: number; + name: string; + code: string; + address: null; + createdAt: string; + updatedAt: string; + deletedAt: null; + bankId: number; + bank: IBankAccountBranchBank; +} + +interface IBankAccountBranchBank { + id: number; + name: string; + shortName: string; +} + +export interface IInventoryPosAccountItemSummary { + id: string; + name: string; + code: string; +} + +export interface IInventoryBankAccountSummary { + id: number; + name: string; + accountNumber: string; + branch: IInventoryBankAccountBranch; +} + +interface IInventoryBankAccountBranch { + id: number; + name: string; + bank: IInventoryBankAccountBranchBank; +} + +interface IInventoryBankAccountBranchBank { + id: number; + name: string; +} diff --git a/src/app/modules/inventories/services/bankAccounts.service.ts b/src/app/modules/inventories/services/bankAccounts.service.ts new file mode 100644 index 0000000..e680718 --- /dev/null +++ b/src/app/modules/inventories/services/bankAccounts.service.ts @@ -0,0 +1,55 @@ +import { IPaginatedResponse } from '@/core/models/service.model'; +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { INVENTORIES_API_ROUTES } from '../constants'; +import { + IInventoryAssignBankAccountRequest, + IInventoryBankAccountRawResponse, + IInventoryBankAccountRequest, + IInventoryBankAccountResponse, +} from '../models/bankAccounts.io'; + +@Injectable({ providedIn: 'root' }) +export class InventoryBankAccountsService { + constructor(private http: HttpClient) {} + + private apiRoutes = INVENTORIES_API_ROUTES; + + getBankAccounts( + inventoryId: number, + ): Observable> { + return this.http.get>( + `${this.apiRoutes.bankAccounts(inventoryId)}`, + ); + } + + create( + inventoryId: number, + payload: IInventoryBankAccountRequest, + ): Observable { + return this.http.post( + `${this.apiRoutes.bankAccounts(inventoryId)}`, + payload, + ); + } + + assign( + inventoryId: number, + payload: IInventoryAssignBankAccountRequest, + ): Observable { + return this.http.post( + `${this.apiRoutes.assignBankAccounts(inventoryId)}`, + payload, + ); + } + unassign( + inventoryId: number, + payload: IInventoryAssignBankAccountRequest, + ): Observable { + return this.http.post( + `${this.apiRoutes.unassignBankAccounts(inventoryId)}`, + payload, + ); + } +} diff --git a/src/app/modules/inventories/services/index.ts b/src/app/modules/inventories/services/index.ts new file mode 100644 index 0000000..796f816 --- /dev/null +++ b/src/app/modules/inventories/services/index.ts @@ -0,0 +1,2 @@ +export * from './bankAccounts.service'; +export * from './main.service'; diff --git a/src/app/modules/inventories/services/posAccounts.service.ts b/src/app/modules/inventories/services/posAccounts.service.ts new file mode 100644 index 0000000..12a35aa --- /dev/null +++ b/src/app/modules/inventories/services/posAccounts.service.ts @@ -0,0 +1,33 @@ +import { IPaginatedResponse } from '@/core/models/service.model'; +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { INVENTORIES_API_ROUTES } from '../constants'; +import { + IInventoryPosAccountRawResponse, + IInventoryPosAccountRequest, + IInventoryPosAccountResponse, +} from '../models'; + +@Injectable({ providedIn: 'root' }) +export class InventoryPosAccountsService { + constructor(private http: HttpClient) {} + + private apiRoutes = INVENTORIES_API_ROUTES; + + getList(inventoryId: number): Observable> { + return this.http.get>( + `${this.apiRoutes.pos.list(String(inventoryId))}`, + ); + } + + create( + inventoryId: number, + payload: IInventoryPosAccountRequest, + ): Observable { + return this.http.post( + `${this.apiRoutes.pos.list(String(inventoryId))}`, + payload, + ); + } +} diff --git a/src/app/modules/inventories/views/index.ts b/src/app/modules/inventories/views/index.ts index bc9dfc9..c2246ae 100644 --- a/src/app/modules/inventories/views/index.ts +++ b/src/app/modules/inventories/views/index.ts @@ -1,2 +1,3 @@ export * from './list.component'; +export * from './posAccounts'; export * from './single.component'; diff --git a/src/app/modules/inventories/views/posAccounts/index.ts b/src/app/modules/inventories/views/posAccounts/index.ts new file mode 100644 index 0000000..576a89e --- /dev/null +++ b/src/app/modules/inventories/views/posAccounts/index.ts @@ -0,0 +1 @@ +export * from './list.component'; diff --git a/src/app/modules/inventories/views/posAccounts/list.component.html b/src/app/modules/inventories/views/posAccounts/list.component.html new file mode 100644 index 0000000..11022d9 --- /dev/null +++ b/src/app/modules/inventories/views/posAccounts/list.component.html @@ -0,0 +1,23 @@ + + +
+ نقاط فروش متصل به انبار +
+ + +
+
+
+ + + @if (inventoryId) { + + } +
diff --git a/src/app/modules/inventories/views/posAccounts/list.component.ts b/src/app/modules/inventories/views/posAccounts/list.component.ts new file mode 100644 index 0000000..0d90b16 --- /dev/null +++ b/src/app/modules/inventories/views/posAccounts/list.component.ts @@ -0,0 +1,76 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, inject, signal } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ButtonDirective } from 'primeng/button'; +import { Card } from 'primeng/card'; +import { InventoryPosAccountFormComponent } from '../../components/posAccounts/form.component'; +import { IInventoryPosAccountResponse } from '../../models'; +import { InventoryPosAccountsService } from '../../services/posAccounts.service'; + +@Component({ + selector: 'app-inventory-pos-account-list', + templateUrl: './list.component.html', + imports: [PageDataListComponent, Card, ButtonDirective, InventoryPosAccountFormComponent], +}) +export class InventoryPosAccountListComponent { + private readonly route = inject(ActivatedRoute); + private readonly service = inject(InventoryPosAccountsService); + + items = signal([]); + loading = signal(false); + visibleForm = signal(false); + inventoryId = this.route.snapshot.params['inventoryId']; + + columns = [] as IColumn[]; + + ngOnInit() { + this.getItems(); + this.columns = [ + { + field: 'name', + header: 'عنوان نقطه فروش', + customDataModel(item) { + return item.name; + }, + }, + { + field: 'bankName', + header: 'حساب بانکی', + customDataModel(item) { + return `${item.bankAccount.name} - بانک ${item.bankAccount.branch.bank.name} - شعبه‌ی ${item.bankAccount.branch.name}`; + }, + }, + + // { + // field: 'actions', + // header: '', + // customDataModel: this.actionsTpl, + // width: '100px', + // }, + ]; + } + + getItems() { + this.loading.set(true); + this.service.getList(this.inventoryId).subscribe({ + next: (res) => { + this.items.set(res.data); + this.loading.set(false); + }, + error: () => { + this.loading.set(false); + }, + }); + } + + refresh() { + this.getItems(); + } + + openAddForm() { + this.visibleForm.set(true); + } +} diff --git a/src/app/modules/inventories/views/single.component.html b/src/app/modules/inventories/views/single.component.html index 8c262b9..7996a39 100644 --- a/src/app/modules/inventories/views/single.component.html +++ b/src/app/modules/inventories/views/single.component.html @@ -7,6 +7,13 @@
+
+ +
diff --git a/src/app/modules/inventories/views/single.component.ts b/src/app/modules/inventories/views/single.component.ts index 036f543..0ee26d5 100644 --- a/src/app/modules/inventories/views/single.component.ts +++ b/src/app/modules/inventories/views/single.component.ts @@ -9,6 +9,7 @@ import { Button, ButtonDirective } from 'primeng/button'; import { Card } from 'primeng/card'; import { GalleriaModule } from 'primeng/galleria'; import { TableModule } from 'primeng/table'; +import { InventoryBankAccountsListComponent } from '../components/bankAccounts/list.component'; import { InventoryMovementListComponent } from '../components/movementList/movement-list.component'; import { IInventoryDetailResponse, IInventoryStockResponse } from '../models'; import { InventoriesService } from '../services/main.service'; @@ -27,6 +28,7 @@ import { InventoriesService } from '../services/main.service'; Card, TableModule, PriceMaskDirective, + InventoryBankAccountsListComponent, ], }) export class InventoryComponent { diff --git a/src/app/modules/pos/store/index.ts b/src/app/modules/pos/store/index.ts index 87e3f60..dbde203 100644 --- a/src/app/modules/pos/store/index.ts +++ b/src/app/modules/pos/store/index.ts @@ -221,8 +221,6 @@ export class POSStore { } setSelectedCustomer(customer: Maybe) { - console.log(customer); - this.setState({ selectedCustomer: customer }); } diff --git a/src/app/shared/catalog/banks/components/select/select.component.ts b/src/app/shared/catalog/banks/components/select/select.component.ts index 7313c26..99cb6a0 100644 --- a/src/app/shared/catalog/banks/components/select/select.component.ts +++ b/src/app/shared/catalog/banks/components/select/select.component.ts @@ -21,8 +21,6 @@ export class BanksSelectComponent extends AbstractSelectComponent this.loading.set(true); this.store.getItems().subscribe({ next: (res) => { - console.log(res); - this.items.set(res || []); this.loading.set(false); }, diff --git a/src/app/shared/components/abstract-form-dialog.component.ts b/src/app/shared/components/abstract-form-dialog.component.ts new file mode 100644 index 0000000..4780b8c --- /dev/null +++ b/src/app/shared/components/abstract-form-dialog.component.ts @@ -0,0 +1,67 @@ +import { ToastService } from '@/core/services/toast.service'; +import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { Subscribable } from 'rxjs'; + +@Component({ + selector: 'abstract-form-dialog', + template: '', + imports: [ReactiveFormsModule], +}) +export abstract class AbstractFormDialog { + @Input() initialValues?: T; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + protected readonly fb = inject(FormBuilder); + constructor(private toastService: ToastService) { + effect(() => { + const v = this.visibleSignal(); + if (!v) this.form.reset(); + }); + } + + abstract form: ReturnType; + + abstract submitForm(payload: U): Subscribable | void; + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.submitForm(this.form.value as U)?.subscribe({ + next: (res) => { + this.toastService.success({ + text: `با موفقیت ایجاد شد`, + }); + this.close(); + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/shared/components/abstract-select.component.ts b/src/app/shared/components/abstract-select.component.ts index d114c68..e534a29 100644 --- a/src/app/shared/components/abstract-select.component.ts +++ b/src/app/shared/components/abstract-select.component.ts @@ -16,6 +16,7 @@ export abstract class AbstractSelectComponent { @Input() canInsert: boolean = false; @Input() showLabel: boolean = true; @Input() showErrors: boolean = true; + @Input() showClear: boolean = true; @Input() isFullDataOptionValue: boolean = false; @Input() optionValue = 'id'; @Output() onChange = new EventEmitter>(); diff --git a/src/app/shared/components/pageDataList/page-data-list.component.ts b/src/app/shared/components/pageDataList/page-data-list.component.ts index 4f9c6f9..114d933 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.ts +++ b/src/app/shared/components/pageDataList/page-data-list.component.ts @@ -119,6 +119,7 @@ export class PageDataListComponent { if (!item || !column) return ''; try { const { field } = column; + if (column.customDataModel) { return this.renderCustom(column, item); }