create amountPercentage field component

This commit is contained in:
2026-04-09 15:01:45 +03:30
parent 359d8cc0a9
commit d154a8402b
11 changed files with 329 additions and 90 deletions
@@ -67,10 +67,6 @@ export class ConsumerCustomerListComponent extends AbstractList<ICustomerRespons
}
toSinglePage(item: ICustomerResponse) {
console.log(item);
console.log(consumerCustomersNamedRoutes.customer.meta);
this.router.navigateByUrl(consumerCustomersNamedRoutes.customer.meta.pagePath!(item.id));
}
}
@@ -10,6 +10,7 @@ export interface ICustomerSaleInvoicesRawResponse {
account: ConsumerAccount;
created_at: string;
items_count: number;
payments: Payments[];
}
export interface ICustomerSaleInvoicesResponse extends ICustomerSaleInvoicesRawResponse {}
@@ -35,3 +36,9 @@ interface Pos extends ISummary {
interface Complex extends ISummary {
business_activity: ISummary;
}
interface Payments {
amount: string;
paid_at: string;
payment_method: string;
}
@@ -32,8 +32,6 @@ export class ConsumerCustomerSaleInvoiceStore extends EntityStore<
breadcrumbItems = computed(() => this._state().breadcrumbItems);
private setBreadcrumb(customerId: string, invoiceId: string) {
console.log(consumerCustomerSaleInvoicesNamedRoutes.saleInvoices.meta.title);
this.patchState({
breadcrumbItems: [
{
@@ -1 +1,35 @@
<div class="flex flex-col gap-6"></div>
<div class="flex flex-col gap-6">
<app-card-data cardTitle="اطلاعات فاکتور" [editable]="true" [(editMode)]="editMode">
<div class="flex flex-col gap-4">
<div class="grid grid-cols-3 gap-4 items-center">
<app-key-value label="کد رهگیری" [value]="invoice()?.code" />
<app-key-value label="تاریخ فاکتور" [value]="invoice()?.invoice_date" type="dateTime" />
<app-key-value label="تاریخ ایجاد فاکتور" [value]="invoice()?.created_at" type="dateTime" />
<div class="col-span-3">
<app-key-value label="توضیحات" [value]="invoice()?.notes" />
</div>
</div>
<p-divider align="center"> اطلاعات پرداخت </p-divider>
<div class="grid grid-cols-3 gap-4 items-center">
<app-key-value label="مجموع قابل پرداخت" [value]="invoice()?.total_amount" type="price" />
@for (payment of invoice()?.payments; track $index) {
<app-key-value
[label]="
`${payment.payment_method === 'SET_OF' ? 'تهاتر' : payment.payment_method === 'TERMINAL' ? 'پایانه' : 'نقدی'}`
"
[value]="payment.amount"
type="price"
/>
}
</div>
<p-divider align="center"> اطلاعات صادر کننده </p-divider>
<div class="grid grid-cols-3 gap-4 items-center">
<app-key-value label="کسب و کار" [value]="invoice()?.pos!.complex.business_activity.name" />
<app-key-value label="مجموعه" [value]="invoice()?.pos!.complex.name" />
<app-key-value label="پایانه‌ی فروش" [value]="invoice()?.pos!.name" />
<app-key-value label="ایجاد کننده" [value]="invoice()?.account?.account?.username" />
</div>
</div>
</app-card-data>
</div>
@@ -3,13 +3,14 @@ import { AppCardComponent, KeyValueComponent } from '@/shared/components';
import pageParamsUtils from '@/utils/page-params.utils';
import { Component, computed, effect, inject, signal } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Divider } from 'primeng/divider';
import { ConsumerCustomerStore } from '../../store/customer.store';
import { ConsumerCustomerSaleInvoiceStore } from '../../store/saleInvoice.store';
@Component({
selector: 'consumer-customer-saleInvoice',
templateUrl: './single.component.html',
imports: [AppCardComponent, KeyValueComponent],
imports: [AppCardComponent, KeyValueComponent, Divider],
})
export class ConsumerCustomerSaleInvoiceComponent {
private readonly route = inject(ActivatedRoute);
@@ -22,11 +23,11 @@ export class ConsumerCustomerSaleInvoiceComponent {
invoiceId = signal<string>(this.pageParams()['invoiceId']);
editMode = signal<boolean>(false);
private readonly customer = computed(() => this.customerStore.entity());
readonly invoice = computed(() => this.store.entity());
constructor() {
effect(() => {
if (this.customer()?.id) {
if (this.invoice()?.id) {
this.setBreadcrumb();
}
});
@@ -2,49 +2,43 @@
<app-input [control]="form.controls.unit_price" name="unit_price" label="قیمت پایه هر گرم" type="price" />
<app-input [control]="form.controls.quantity" name="quantity" label="مقدار" type="number" suffix="گرم" />
<app-enum-select [control]="form.controls.payload.controls.karat" type="goldKarat" />
<app-input
[control]="form.controls.payload.controls.wages"
<app-amount-percentage-input
[percentageControl]="form.controls.payload.controls.wages_percentage"
[amountControl]="form.controls.payload.controls.wages_amount"
[baseAmount]="unitWithQuantity()"
name="wages"
[label]="`اجرت ${toPriceFormat(wagePrice())}`"
type="number"
suffix="درصد"
[min]="0"
[max]="100"
label="اجرت"
/>
<app-input
[control]="form.controls.payload.controls.commission"
<app-amount-percentage-input
[percentageControl]="form.controls.payload.controls.commission_percentage"
[amountControl]="form.controls.payload.controls.commission_amount"
[baseAmount]="unitWithQuantity()"
name="commission"
[label]="`حق‌العمل ${toPriceFormat(commissionPrice())}`"
type="number"
suffix="درصد"
[min]="0"
[max]="100"
label="حق‌العمل"
/>
<app-input
[control]="form.controls.payload.controls.profit"
<app-amount-percentage-input
[percentageControl]="form.controls.payload.controls.profit_percentage"
[amountControl]="form.controls.payload.controls.profit_amount"
[baseAmount]="totalAmountBeforeProfit()"
name="profit"
[label]="`سود ${toPriceFormat(profitPrice())}`"
type="number"
suffix="درصد"
[min]="0"
[max]="100"
label="سود"
/>
<app-input
[control]="form.controls.discount"
<app-amount-percentage-input
[percentageControl]="form.controls.discount_percentage"
[amountControl]="form.controls.discount_amount"
[baseAmount]="baseTotalAmount()"
name="discount"
label="تخفیف"
type="number"
suffix="درصد"
[min]="0"
[max]="100"
/>
<hr />
<div class="flex flex-col gap-4 justify-center w-full">
<pos-form-dialog-amount-card-template
[totalAmount]="totalAmount()"
[baseTotalAmount]="baseTotalAmount()"
[discountAmount]="discountAmount()"
[discountAmount]="form.controls.discount_amount.value || 0"
[taxAmount]="taxAmount()"
/>
<p-button (onClick)="submit()"> افزودن به لیست خرید </p-button>
@@ -1,9 +1,10 @@
import { AbstractForm } from '@/shared/abstractClasses';
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
import { InputComponent } from '@/shared/components';
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat';
import { formatWithCurrency } from '@/utils';
import { Component, EventEmitter, Output, signal } from '@angular/core';
import { Component, computed, EventEmitter, Output } from '@angular/core';
import { ReactiveFormsModule, Validators } from '@angular/forms';
import { Button } from 'primeng/button';
import { IGoldPayload, IPosOrderItem } from '../../../models';
@@ -18,6 +19,7 @@ import { PosFormDialogAmountCardTemplateComponent } from '../form-dialog-amount-
EnumSelectComponent,
Button,
PosFormDialogAmountCardTemplateComponent,
AmountPercentageInputComponent,
],
})
export class PosGoldPayloadFormComponent extends AbstractForm<
@@ -31,19 +33,22 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
const form = this.fb.group({
unit_price: [this.initialValues?.unit_price || 200_000_000, [Validators.required]],
quantity: [this.initialValues?.quantity || 2, [Validators.required]],
discount: [this.initialValues?.discount || 0],
discount_amount: [this.initialValues?.discount || 0],
discount_percentage: [this.initialValues?.discount || 0],
payload: this.fb.group({
commission: [this.initialValues?.payload?.commission || 10, [Validators.required]],
commission_amount: [this.initialValues?.payload?.commission || 0, [Validators.required]],
commission_percentage: [
this.initialValues?.payload?.commission || 10,
[Validators.required],
],
wages_amount: [this.initialValues?.payload?.wages || 0, [Validators.required]],
wages_percentage: [this.initialValues?.payload?.wages || 10, [Validators.required]],
profit_amount: [this.initialValues?.payload?.profit || 0, [Validators.required]],
profit_percentage: [this.initialValues?.payload?.profit || 10, [Validators.required]],
karat: [this.initialValues?.payload?.karat || '', [Validators.required]],
wages: [this.initialValues?.payload?.wages || 10, [Validators.required]],
profit: [this.initialValues?.payload?.profit || 10, [Validators.required]],
}),
});
form.valueChanges.subscribe((value) => {
this.updateCalculateAmount(value as IPosOrderItem<IGoldPayload>);
});
return form;
};
@@ -53,56 +58,51 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
...payload,
total_amount: this.totalAmount(),
base_total_amount: this.baseTotalAmount(),
discount_amount: this.discountAmount(),
tax_amount: this.taxAmount(),
payload: {
commission_amount: this.commissionPrice(),
profit_amount: this.profitPrice(),
wages_amount: this.wagePrice(),
commission: this.form.controls.payload.controls.commission.value || 0,
commission: this.form.controls.payload.controls.commission_amount.value || 0,
karat: this.form.controls.payload.controls.karat.value as TGoldKarat,
profit: this.form.controls.payload.controls.profit.value || 0,
wages: this.form.controls.payload.controls.wages.value || 0,
profit: this.form.controls.payload.controls.profit_amount.value || 0,
wages: this.form.controls.payload.controls.wages_amount.value || 0,
},
});
}
wagePrice = signal<number>(0);
karatPrice = signal<number>(0);
commissionPrice = signal<number>(0);
profitPrice = signal<number>(0);
totalAmount = signal<number>(0);
baseTotalAmount = signal<number>(0);
discountAmount = signal<number>(0);
taxAmount = signal<number>(0);
unitWithQuantity = computed(
() => (this.form.value.unit_price ?? 0) * (this.form.value.quantity ?? 0),
);
toPriceFormat(amount: number) {
totalAmountBeforeProfit = computed(() => {
const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0');
const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0');
return this.unitWithQuantity() + wageAmount + commissionAmount;
});
baseTotalAmount = computed(() => {
const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0');
const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0');
const profitAmount = Number(this.form.value.payload?.profit_amount ?? '0');
return this.unitWithQuantity() + profitAmount + commissionAmount + wageAmount;
});
taxAmount = computed(() => {
const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0');
const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0');
const profitAmount = Number(this.form.value.payload?.profit_amount ?? '0');
const discountAmount = Number(this.form.value.discount_amount ?? '0');
return (profitAmount + commissionAmount + wageAmount - discountAmount) * 0.1;
});
totalAmount = computed(() => {
const discountAmount = Number(this.form.value.discount_amount ?? '0');
return this.baseTotalAmount() - discountAmount + this.taxAmount();
});
toAmountFormat(amount: number) {
if (!amount) {
return '';
}
return `(${formatWithCurrency(amount)})`;
}
updateCalculateAmount(value: Partial<IPosOrderItem<IGoldPayload>>) {
const unitWithQuantity = (value.unit_price ?? 0) * (value.quantity ?? 0);
const commissionPrice = (unitWithQuantity * Number(value.payload?.commission ?? '0')) / 100;
const wagePrice = (unitWithQuantity * Number(value.payload?.wages ?? '0')) / 100;
const totalPriceBeforeProfit = unitWithQuantity + wagePrice + commissionPrice;
const profitPrice = (totalPriceBeforeProfit * Number(value.payload?.profit ?? '0')) / 100;
const baseTotalAmount = unitWithQuantity + profitPrice + commissionPrice + wagePrice;
const discountPrice = (baseTotalAmount * Number(value.discount || '0')) / 100;
const taxAmount = (profitPrice + commissionPrice + wagePrice - discountPrice) * 0.1;
this.wagePrice.set(wagePrice);
this.commissionPrice.set(commissionPrice);
this.profitPrice.set(profitPrice);
this.discountAmount.set(discountPrice);
this.taxAmount.set(taxAmount);
this.baseTotalAmount.set(baseTotalAmount);
this.totalAmount.set(baseTotalAmount - discountPrice + taxAmount);
}
}
@@ -5,9 +5,9 @@ export interface IGoldPayload {
wages: number;
profit: number;
commission: number;
wages_amount: number;
profit_amount: number;
commission_amount: number;
// wages_amount: number;
// profit_amount: number;
// commission_amount: number;
}
export interface IStandardPayload {}
@@ -43,7 +43,7 @@ export const INITIAL_POS_STATE: IPosLandingState = {
customerDetails: {
type: CustomerType.UNKNOWN,
},
invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC),
invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC_WITH_FULL_TIME),
payments: {
cash: 0,
set_off: 0,