feat: enhance input component to support price type and add new payment method components
- Updated InputComponent to handle 'price' type with appropriate formatting and validation. - Introduced new API routes for purchase receipt payments. - Created constants for purchase receipts and invoices. - Developed SupplierInvoicePayFormComponent for processing invoice payments. - Implemented SupplierLedgerComponent to display supplier transactions. - Added services and stores for managing supplier invoices and payments. - Created components for selecting payment methods and displaying payment type tags. - Enhanced UI with new templates for invoices and payment forms. - Added utility functions for handling payment method types and statuses.
This commit is contained in:
@@ -5,7 +5,9 @@ import { catchError, map } from 'rxjs/operators';
|
||||
import { IBankResponse } from '../models';
|
||||
import { BanksService } from '../services/main.service';
|
||||
|
||||
export interface banksState extends BaseState<IBankResponse[]> {}
|
||||
export interface banksState extends BaseState<IBankResponse[]> {
|
||||
items: IBankResponse[];
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './gender';
|
||||
export * from './movementTypes';
|
||||
export * from './purchaseReceiptStatus';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './select/select.component';
|
||||
export * from './tag/tag.component';
|
||||
@@ -0,0 +1,13 @@
|
||||
<uikit-field label="نوع پرداخت" [control]="control" [showLabel]="showLabel" [showErrors]="showErrors">
|
||||
<p-select
|
||||
[options]="items"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="انتخاب نوع پرداخت"
|
||||
[formControl]="control"
|
||||
[showClear]="true"
|
||||
[filter]="true"
|
||||
appendTo="body"
|
||||
>
|
||||
</p-select>
|
||||
</uikit-field>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { UikitFieldComponent } from '@/uikit';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { Select } from 'primeng/select';
|
||||
import { PaymentMethodType } from '../../types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-catalog-payment-method-type-select-field',
|
||||
templateUrl: './select.component.html',
|
||||
imports: [ReactiveFormsModule, Select, UikitFieldComponent],
|
||||
})
|
||||
export class PaymentMethodTypeSelectComponent {
|
||||
@Input() control = new FormControl<Maybe<PaymentMethodType>>(null);
|
||||
@Input() showLabel: boolean = true;
|
||||
@Input() showErrors: boolean = true;
|
||||
@Input() showClear: boolean = false;
|
||||
@Input() isFullDataOptionValue: boolean = false;
|
||||
@Output() onChange = new EventEmitter<Maybe<PaymentMethodType>>();
|
||||
|
||||
items = [
|
||||
{ label: 'نقدی', value: 'CASH' },
|
||||
{ label: 'کارت بانکی', value: 'CARD' },
|
||||
{ label: 'حساب', value: 'BANK' },
|
||||
{ label: 'چک', value: 'CHECK' },
|
||||
{ label: 'سایر', value: 'OTHER' },
|
||||
] as { label: string; value: PaymentMethodType }[];
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<p-tag [severity]="color">
|
||||
{{ text }}
|
||||
</p-tag>
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Tag } from 'primeng/tag';
|
||||
import { PaymentMethodType } from '../../types';
|
||||
import { getPaymentMethodTypeColor, getPaymentMethodTypeText } from '../../utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-catalog-payment-method-type-tag',
|
||||
templateUrl: './tag.component.html',
|
||||
imports: [Tag],
|
||||
})
|
||||
export class CatalogPaymentMethodTypeTagComponent {
|
||||
@Input() type!: PaymentMethodType;
|
||||
constructor() {}
|
||||
|
||||
get text() {
|
||||
return getPaymentMethodTypeText(this.type);
|
||||
}
|
||||
get color() {
|
||||
return getPaymentMethodTypeColor(this.type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './components/tag/tag.component';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
@@ -0,0 +1,9 @@
|
||||
export const PaymentMethodType = {
|
||||
CASH: 'CASH',
|
||||
CARD: 'CARD',
|
||||
BANK: 'BANK',
|
||||
CHECK: 'CHECK',
|
||||
OTHER: 'OTHER',
|
||||
} as const;
|
||||
|
||||
export type PaymentMethodType = (typeof PaymentMethodType)[keyof typeof PaymentMethodType];
|
||||
@@ -0,0 +1,32 @@
|
||||
import { TagSeverity } from '@/core/models/tag';
|
||||
import { PaymentMethodType } from './types';
|
||||
|
||||
export const getPaymentMethodTypeText = (value: PaymentMethodType): string => {
|
||||
switch (value) {
|
||||
case PaymentMethodType.CASH:
|
||||
return 'نقدی';
|
||||
case PaymentMethodType.CARD:
|
||||
return 'کارت';
|
||||
case PaymentMethodType.BANK:
|
||||
return 'بانک';
|
||||
case PaymentMethodType.CHECK:
|
||||
return 'چک';
|
||||
case PaymentMethodType.OTHER:
|
||||
return 'سایر';
|
||||
}
|
||||
};
|
||||
|
||||
export const getPaymentMethodTypeColor = (value: PaymentMethodType): TagSeverity => {
|
||||
switch (value) {
|
||||
case PaymentMethodType.CASH:
|
||||
return 'danger';
|
||||
case PaymentMethodType.CARD:
|
||||
return 'info';
|
||||
case PaymentMethodType.BANK:
|
||||
return 'success';
|
||||
case PaymentMethodType.CHECK:
|
||||
return 'warn';
|
||||
case PaymentMethodType.OTHER:
|
||||
return 'secondary';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './tag/tag.component';
|
||||
@@ -0,0 +1,3 @@
|
||||
<p-tag [severity]="color">
|
||||
{{ text }}
|
||||
</p-tag>
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Tag } from 'primeng/tag';
|
||||
import { PaymentType } from '../../types';
|
||||
import { getPaymentTypeColor, getPaymentTypeText } from '../../utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-catalog-payment-type-tag',
|
||||
templateUrl: './tag.component.html',
|
||||
imports: [Tag],
|
||||
})
|
||||
export class CatalogPaymentTypeTagComponent {
|
||||
@Input() type!: PaymentType;
|
||||
constructor() {}
|
||||
|
||||
get text() {
|
||||
return getPaymentTypeText(this.type);
|
||||
}
|
||||
get color() {
|
||||
return getPaymentTypeColor(this.type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './components/tag/tag.component';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
@@ -0,0 +1,6 @@
|
||||
export const PaymentType = {
|
||||
PAYMENT: 'PAYMENT',
|
||||
REFUND: 'REFUND',
|
||||
} as const;
|
||||
|
||||
export type PaymentType = (typeof PaymentType)[keyof typeof PaymentType];
|
||||
@@ -0,0 +1,20 @@
|
||||
import { TagSeverity } from '@/core/models/tag';
|
||||
import { PaymentType } from './types';
|
||||
|
||||
export const getPaymentTypeText = (value: PaymentType): string => {
|
||||
switch (value) {
|
||||
case PaymentType.PAYMENT:
|
||||
return 'پرداخت';
|
||||
case PaymentType.REFUND:
|
||||
return 'بازگشت وجه';
|
||||
}
|
||||
};
|
||||
|
||||
export const getPaymentTypeColor = (value: PaymentType): TagSeverity => {
|
||||
switch (value) {
|
||||
case PaymentType.PAYMENT:
|
||||
return 'success';
|
||||
case PaymentType.REFUND:
|
||||
return 'warn';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './tag.component';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
@@ -0,0 +1,3 @@
|
||||
<p-tag [severity]="color">
|
||||
{{ text }}
|
||||
</p-tag>
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Tag } from 'primeng/tag';
|
||||
import { PurchaseReceiptStatus } from './types';
|
||||
import { getPurchaseReceiptStatusColor, getPurchaseReceiptStatusText } from './utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-catalog-purchase-receipt-status-tag',
|
||||
templateUrl: './tag.component.html',
|
||||
imports: [Tag],
|
||||
})
|
||||
export class CatalogPurchaseReceiptStatusTagComponent {
|
||||
@Input() type!: PurchaseReceiptStatus;
|
||||
constructor() {}
|
||||
|
||||
get text() {
|
||||
return getPurchaseReceiptStatusText(this.type);
|
||||
}
|
||||
get color() {
|
||||
return getPurchaseReceiptStatusColor(this.type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export const PurchaseReceiptStatus = {
|
||||
UNPAID: 'UNPAID',
|
||||
PARTIALLY_PAID: 'PARTIALLY_PAID',
|
||||
PAID: 'PAID',
|
||||
} as const;
|
||||
|
||||
export type PurchaseReceiptStatus =
|
||||
(typeof PurchaseReceiptStatus)[keyof typeof PurchaseReceiptStatus];
|
||||
@@ -0,0 +1,24 @@
|
||||
import { TagSeverity } from '@/core/models/tag';
|
||||
import { PurchaseReceiptStatus } from './types';
|
||||
|
||||
export const getPurchaseReceiptStatusText = (value: PurchaseReceiptStatus): string => {
|
||||
switch (value) {
|
||||
case PurchaseReceiptStatus.UNPAID:
|
||||
return 'پرداخت نشده';
|
||||
case PurchaseReceiptStatus.PARTIALLY_PAID:
|
||||
return 'پرداخت شده جزئی';
|
||||
case PurchaseReceiptStatus.PAID:
|
||||
return 'تسویه شده';
|
||||
}
|
||||
};
|
||||
|
||||
export const getPurchaseReceiptStatusColor = (value: PurchaseReceiptStatus): TagSeverity => {
|
||||
switch (value) {
|
||||
case PurchaseReceiptStatus.UNPAID:
|
||||
return 'danger';
|
||||
case PurchaseReceiptStatus.PARTIALLY_PAID:
|
||||
return 'warn';
|
||||
case PurchaseReceiptStatus.PAID:
|
||||
return 'success';
|
||||
}
|
||||
};
|
||||
@@ -8,8 +8,8 @@ import { Subscribable } from 'rxjs';
|
||||
template: '',
|
||||
imports: [ReactiveFormsModule],
|
||||
})
|
||||
export abstract class AbstractFormDialog<T, U> {
|
||||
@Input() initialValues?: T;
|
||||
export abstract class AbstractFormDialog<Response, Request> {
|
||||
@Input() initialValues?: Response;
|
||||
|
||||
@Input()
|
||||
set visible(v: boolean) {
|
||||
@@ -21,28 +21,34 @@ export abstract class AbstractFormDialog<T, U> {
|
||||
private visibleSignal = signal(false);
|
||||
|
||||
@Output() visibleChange = new EventEmitter<boolean>();
|
||||
@Output() onSubmit = new EventEmitter<T>();
|
||||
@Output() onSubmit = new EventEmitter<Response>();
|
||||
|
||||
protected readonly fb = inject(FormBuilder);
|
||||
constructor(private toastService: ToastService) {
|
||||
effect(() => {
|
||||
const v = this.visibleSignal();
|
||||
if (!v) this.form.reset();
|
||||
if (!v) this.form.reset(this.defaultValues);
|
||||
});
|
||||
}
|
||||
|
||||
abstract form: ReturnType<FormBuilder['group']>;
|
||||
defaultValues: Partial<Request> = {};
|
||||
|
||||
abstract submitForm(payload: U): Subscribable<T> | void;
|
||||
abstract submitForm(payload: Request): Subscribable<Response> | void;
|
||||
|
||||
submitLoading = signal(false);
|
||||
|
||||
submit() {
|
||||
console.log('first');
|
||||
|
||||
this.form.markAllAsTouched();
|
||||
if (this.form.valid) {
|
||||
console.log('isValid');
|
||||
console.log(this.submitForm);
|
||||
|
||||
this.form.disable();
|
||||
this.submitLoading.set(true);
|
||||
this.submitForm(this.form.value as U)?.subscribe({
|
||||
this.submitForm(this.form.value as Request)?.subscribe({
|
||||
next: (res) => {
|
||||
this.toastService.success({
|
||||
text: `با موفقیت ایجاد شد`,
|
||||
@@ -57,6 +63,11 @@ export abstract class AbstractFormDialog<T, U> {
|
||||
this.submitLoading.set(false);
|
||||
this.form.enable();
|
||||
},
|
||||
complete: () => {
|
||||
this.submitLoading.set(false);
|
||||
this.form.enable();
|
||||
console.log('end');
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="flex items-center">
|
||||
<div class="grow">
|
||||
<div class="flex items-center gap-2">
|
||||
@if (backRoute) {
|
||||
<p-button icon="pi pi-arrow-right" aria-label="بازگشت" outlined [routerLink]="backRoute" />
|
||||
}
|
||||
<h3 class="m-0">{{ pageTitle }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
@if (actions) {
|
||||
<div class="flex items-center gap-2">
|
||||
<ng-container [ngTemplateOutlet]="actions"> </ng-container>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { NgTemplateOutlet } from '@angular/common';
|
||||
import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
|
||||
import { RouterLink, UrlTree } from '@angular/router';
|
||||
import { Button } from 'primeng/button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-inner-pages-header',
|
||||
templateUrl: './inner-pages-header.component.html',
|
||||
imports: [Button, RouterLink, NgTemplateOutlet],
|
||||
})
|
||||
export class InnerPagesHeaderComponent {
|
||||
@Input() pageTitle!: string;
|
||||
@Input() backRoute?: UrlTree | string | any[];
|
||||
@ContentChild('actions', { static: true }) actions?: Maybe<TemplateRef<any>>;
|
||||
constructor() {}
|
||||
}
|
||||
@@ -1,6 +1,24 @@
|
||||
<uikit-field [label]="label" [name]="name" [control]="control" [showLabel]="!!label" [showErrors]="showErrors">
|
||||
@if (type === "switch") {
|
||||
<p-toggleSwitch [formControl]="control" />
|
||||
} @else if (type === "price") {
|
||||
<p-inputgroup>
|
||||
<p-inputnumber
|
||||
[attr.id]="name"
|
||||
[formControl]="control"
|
||||
[attr.name]="name"
|
||||
[attr.placeholder]="placeholder"
|
||||
[attr.inputmode]="inputMode"
|
||||
[attr.maxlength]="maxLength || null"
|
||||
[attr.type]="htmlType"
|
||||
[attr.autocomplete]="autocomplete"
|
||||
[class]="` w-full ${size === 'large' ? 'p-inputtext-lg' : size === 'small' ? 'p-inputtext-sm' : ''}`"
|
||||
[required]="isRequired"
|
||||
[invalid]="control.invalid && (control.touched || control.dirty)"
|
||||
(input)="onInput($event)"
|
||||
/>
|
||||
<p-inputgroup-addon>ریال</p-inputgroup-addon>
|
||||
</p-inputgroup>
|
||||
} @else {
|
||||
<input
|
||||
pInputText
|
||||
@@ -18,4 +36,7 @@
|
||||
(input)="onInput($event)"
|
||||
/>
|
||||
}
|
||||
@if (hint) {
|
||||
<small [attr.id]="name + '-help'">{{ hint }}</small>
|
||||
}
|
||||
</uikit-field>
|
||||
|
||||
@@ -1,20 +1,41 @@
|
||||
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';
|
||||
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { InputGroupModule } from 'primeng/inputgroup';
|
||||
import { InputGroupAddon } from 'primeng/inputgroupaddon';
|
||||
import { InputNumberModule } from 'primeng/inputnumber';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { ToggleSwitch } from 'primeng/toggleswitch';
|
||||
|
||||
@Component({
|
||||
selector: 'app-input',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule, InputTextModule, UikitFieldComponent, ToggleSwitch],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
InputTextModule,
|
||||
InputGroupModule,
|
||||
UikitFieldComponent,
|
||||
ToggleSwitch,
|
||||
PriceMaskDirective,
|
||||
InputGroupAddon,
|
||||
InputNumberModule,
|
||||
],
|
||||
templateUrl: './input.component.html',
|
||||
})
|
||||
export class InputComponent {
|
||||
@Input() type: 'simple' | 'postalCode' | 'mobile' | 'phone' | 'email' | 'checkbox' | 'switch' =
|
||||
'simple';
|
||||
@Input() type:
|
||||
| 'simple'
|
||||
| 'postalCode'
|
||||
| 'mobile'
|
||||
| 'phone'
|
||||
| 'email'
|
||||
| 'checkbox'
|
||||
| 'switch'
|
||||
| 'price' = 'simple';
|
||||
@Input() control!: FormControl<Maybe<any>>;
|
||||
@Input() name!: string;
|
||||
@Input() label: string = '';
|
||||
@@ -24,11 +45,12 @@ export class InputComponent {
|
||||
@Input() size?: 'small' | 'large';
|
||||
@Input() autocomplete?: string = 'off';
|
||||
@Input() showErrors = false;
|
||||
@Output() valueChange = new EventEmitter<string>();
|
||||
@Input() hint?: string;
|
||||
@Output() valueChange = new EventEmitter<string | number>();
|
||||
|
||||
onInput(ev: Event) {
|
||||
const v = (ev.target as HTMLInputElement).value;
|
||||
this.valueChange.emit(v);
|
||||
this.valueChange.emit(this.type === 'price' ? parseFloat(v) : v);
|
||||
}
|
||||
|
||||
get placeholder(): string | null {
|
||||
@@ -42,6 +64,8 @@ export class InputComponent {
|
||||
return 'شماره تلفن';
|
||||
case 'email':
|
||||
return 'آدرس ایمیل خود را وارد کنید';
|
||||
case 'price':
|
||||
return 'مبلغ';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@@ -52,6 +76,7 @@ export class InputComponent {
|
||||
case 'mobile':
|
||||
case 'phone':
|
||||
case 'postalCode':
|
||||
case 'price':
|
||||
return 'numeric';
|
||||
default:
|
||||
return 'text';
|
||||
@@ -75,6 +100,7 @@ export class InputComponent {
|
||||
switch (this.type) {
|
||||
case 'mobile':
|
||||
case 'phone':
|
||||
case 'price':
|
||||
return 'ltrInput';
|
||||
case 'email':
|
||||
case 'postalCode':
|
||||
|
||||
Reference in New Issue
Block a user