feat: add correction and return from sale functionalities to sale invoices
- Added new API routes for correction and return from sale in POS_SALE_INVOICES_API_ROUTES. - Implemented correction and return from sale methods in PosSaleInvoicesService. - Updated PosSaleInvoiceStore to include actions for correction and return from sale. - Enhanced single.component to handle correction and return from sale events. - Created correction and return from sale forms with appropriate validation and submission logic. - Updated sale-invoice-single-view component to manage correction and return from sale actions. - Added models for correction and return from sale requests. - Improved user interface messages and form handling for better user experience.
This commit is contained in:
@@ -6,6 +6,8 @@ export const POS_SALE_INVOICES_API_ROUTES = {
|
|||||||
tsp: {
|
tsp: {
|
||||||
send: (invoiceId: string) => `${baseUrl()}/${invoiceId}/send`,
|
send: (invoiceId: string) => `${baseUrl()}/${invoiceId}/send`,
|
||||||
getInquiry: (invoiceId: string) => `${baseUrl()}/${invoiceId}/inquiry`,
|
getInquiry: (invoiceId: string) => `${baseUrl()}/${invoiceId}/inquiry`,
|
||||||
|
correction: (invoiceId: string) => `${baseUrl()}/${invoiceId}/correction`,
|
||||||
|
returnFromSale: (invoiceId: string) => `${baseUrl()}/${invoiceId}/return_from_sale`,
|
||||||
revoke: (invoiceId: string) => `${baseUrl()}/${invoiceId}/revoke`,
|
revoke: (invoiceId: string) => `${baseUrl()}/${invoiceId}/revoke`,
|
||||||
retry: (invoiceId: string) => `${baseUrl()}/${invoiceId}/retry`,
|
retry: (invoiceId: string) => `${baseUrl()}/${invoiceId}/retry`,
|
||||||
status: (invoiceId: string) => `${baseUrl()}/${invoiceId}/fiscal/status`,
|
status: (invoiceId: string) => `${baseUrl()}/${invoiceId}/fiscal/status`,
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { IPosOrderItem } from '../../shop/models';
|
||||||
|
|
||||||
|
export interface IPosCorrectionRequest {
|
||||||
|
total_amount: number;
|
||||||
|
discount_amount: number;
|
||||||
|
tax_amount: number;
|
||||||
|
invoice_date: string;
|
||||||
|
items: IPosOrderItem[];
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { IPosOrderItem } from '../../shop/models';
|
||||||
|
|
||||||
|
export interface IPosReturnFromSaleRequest {
|
||||||
|
total_amount: number;
|
||||||
|
discount_amount: number;
|
||||||
|
tax_amount: number;
|
||||||
|
invoice_date: string;
|
||||||
|
items: IPosOrderItem[];
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
@@ -21,11 +21,11 @@ export class PosSaleInvoicesService {
|
|||||||
private apiRoutes = POS_SALE_INVOICES_API_ROUTES;
|
private apiRoutes = POS_SALE_INVOICES_API_ROUTES;
|
||||||
|
|
||||||
getAll(
|
getAll(
|
||||||
query: IPosSaleInvoicesFilterDto = {},
|
query: IPosSaleInvoicesFilterDto = {}
|
||||||
): Observable<IPaginatedResponse<IPosSaleInvoicesSummaryResponse>> {
|
): Observable<IPaginatedResponse<IPosSaleInvoicesSummaryResponse>> {
|
||||||
return this.http.get<IPaginatedResponse<IPosSaleInvoicesSummaryRawResponse>>(
|
return this.http.get<IPaginatedResponse<IPosSaleInvoicesSummaryRawResponse>>(
|
||||||
this.apiRoutes.list(),
|
this.apiRoutes.list(),
|
||||||
{ params: query as Record<string, string | number | boolean> },
|
{ params: query as Record<string, string | number | boolean> }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,14 +36,14 @@ export class PosSaleInvoicesService {
|
|||||||
sendToTsp(invoiceId: string): Observable<IPosSaleInvoiceFiscalActionResponse> {
|
sendToTsp(invoiceId: string): Observable<IPosSaleInvoiceFiscalActionResponse> {
|
||||||
return this.http.post<IPosSaleInvoiceFiscalActionResponse>(
|
return this.http.post<IPosSaleInvoiceFiscalActionResponse>(
|
||||||
this.apiRoutes.tsp.send(invoiceId),
|
this.apiRoutes.tsp.send(invoiceId),
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
retrySendToTsp(invoiceId: string): Observable<IPosSaleInvoiceFiscalActionResponse> {
|
retrySendToTsp(invoiceId: string): Observable<IPosSaleInvoiceFiscalActionResponse> {
|
||||||
return this.http.post<IPosSaleInvoiceFiscalActionResponse>(
|
return this.http.post<IPosSaleInvoiceFiscalActionResponse>(
|
||||||
this.apiRoutes.tsp.retry(invoiceId),
|
this.apiRoutes.tsp.retry(invoiceId),
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,20 +54,34 @@ export class PosSaleInvoicesService {
|
|||||||
getInquiry(invoiceId: string): Observable<IPosSaleInvoiceFiscalStatusResponse> {
|
getInquiry(invoiceId: string): Observable<IPosSaleInvoiceFiscalStatusResponse> {
|
||||||
return this.http.get<IPosSaleInvoiceFiscalStatusResponse>(
|
return this.http.get<IPosSaleInvoiceFiscalStatusResponse>(
|
||||||
this.apiRoutes.tsp.getInquiry(invoiceId),
|
this.apiRoutes.tsp.getInquiry(invoiceId),
|
||||||
{},
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
correction(data: any, invoiceId: string): Observable<IPosSaleInvoiceFiscalActionResponse> {
|
||||||
|
return this.http.post<IPosSaleInvoiceFiscalActionResponse>(
|
||||||
|
this.apiRoutes.tsp.correction(invoiceId),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnFromSale(data: any, invoiceId: string): Observable<IPosSaleInvoiceFiscalActionResponse> {
|
||||||
|
return this.http.post<IPosSaleInvoiceFiscalActionResponse>(
|
||||||
|
this.apiRoutes.tsp.returnFromSale(invoiceId),
|
||||||
|
data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
revoke(invoiceId: string): Observable<IPosSaleInvoiceFiscalStatusResponse> {
|
revoke(invoiceId: string): Observable<IPosSaleInvoiceFiscalStatusResponse> {
|
||||||
return this.http.post<IPosSaleInvoiceFiscalStatusResponse>(
|
return this.http.post<IPosSaleInvoiceFiscalStatusResponse>(
|
||||||
this.apiRoutes.tsp.revoke(invoiceId),
|
this.apiRoutes.tsp.revoke(invoiceId),
|
||||||
{},
|
{}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFiscalAttempts(invoiceId: string): Observable<IPosSaleInvoiceFiscalAttemptsResponse> {
|
getFiscalAttempts(invoiceId: string): Observable<IPosSaleInvoiceFiscalAttemptsResponse> {
|
||||||
return this.http.get<IPosSaleInvoiceFiscalAttemptsResponse>(
|
return this.http.get<IPosSaleInvoiceFiscalAttemptsResponse>(
|
||||||
this.apiRoutes.tsp.attempts(invoiceId),
|
this.apiRoutes.tsp.attempts(invoiceId)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { TspProviderResponseStatus } from '@/shared/catalog';
|
|||||||
import taxProviderStatusTranslatorUtil from '@/shared/catalog/taxProviderStatus/tax-provider-status-translator.util';
|
import taxProviderStatusTranslatorUtil from '@/shared/catalog/taxProviderStatus/tax-provider-status-translator.util';
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { catchError, finalize } from 'rxjs';
|
import { catchError, finalize } from 'rxjs';
|
||||||
|
import { IPosCorrectionRequest } from '../models/correction';
|
||||||
|
import { IPosReturnFromSaleRequest } from '../models/returnFromSale';
|
||||||
import { PosSaleInvoicesService } from '../services/main.service';
|
import { PosSaleInvoicesService } from '../services/main.service';
|
||||||
|
|
||||||
interface PosSaleInvoiceState extends EntityState<ISaleInvoiceFullResponse> {}
|
interface PosSaleInvoiceState extends EntityState<ISaleInvoiceFullResponse> {}
|
||||||
@@ -53,6 +55,12 @@ export class PosSaleInvoiceStore extends EntityStore<
|
|||||||
sendToTsp(invoceId: string) {
|
sendToTsp(invoceId: string) {
|
||||||
return this.service.sendToTsp(invoceId);
|
return this.service.sendToTsp(invoceId);
|
||||||
}
|
}
|
||||||
|
correction(data: IPosCorrectionRequest, invoceId: string) {
|
||||||
|
return this.service.correction(data, invoceId);
|
||||||
|
}
|
||||||
|
returnFromSale(data: IPosReturnFromSaleRequest, invoceId: string) {
|
||||||
|
return this.service.returnFromSale(data, invoceId);
|
||||||
|
}
|
||||||
inquiry(invoceId: string) {
|
inquiry(invoceId: string) {
|
||||||
return this.service.getInquiry(invoceId);
|
return this.service.getInquiry(invoceId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,7 @@
|
|||||||
[inquiryLoading]="inquiryLoading()"
|
[inquiryLoading]="inquiryLoading()"
|
||||||
(onSendToTsp)="sendToTsp()"
|
(onSendToTsp)="sendToTsp()"
|
||||||
(onInquiry)="inquiry()"
|
(onInquiry)="inquiry()"
|
||||||
|
(onCorrection)="correction($event)"
|
||||||
|
(onReturnFromSale)="returnFromSale($event)"
|
||||||
variant="pos" />
|
variant="pos" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { Component, computed, inject, signal } from '@angular/core';
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { finalize } from 'rxjs';
|
import { finalize } from 'rxjs';
|
||||||
import { posSaleInvoicesNamedRoutes } from '../constants';
|
import { posSaleInvoicesNamedRoutes } from '../constants';
|
||||||
|
import { IPosCorrectionRequest } from '../models/correction';
|
||||||
|
import { IPosReturnFromSaleRequest } from '../models/returnFromSale';
|
||||||
import { PosSaleInvoiceStore } from '../store/main.store';
|
import { PosSaleInvoiceStore } from '../store/main.store';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -78,10 +80,10 @@ export class PosSaleInvoiceComponent {
|
|||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
correction() {
|
correction(data: IPosCorrectionRequest) {
|
||||||
this.correctionLoading.set(true);
|
this.correctionLoading.set(true);
|
||||||
this.store
|
this.store
|
||||||
.sendToTsp(this.invoiceId())
|
.correction(data, this.invoiceId())
|
||||||
.pipe(
|
.pipe(
|
||||||
finalize(() => {
|
finalize(() => {
|
||||||
this.correctionLoading.set(false);
|
this.correctionLoading.set(false);
|
||||||
@@ -89,10 +91,10 @@ export class PosSaleInvoiceComponent {
|
|||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
backFromSale() {
|
returnFromSale(data: IPosReturnFromSaleRequest) {
|
||||||
this.backFromSaleLoading.set(true);
|
this.backFromSaleLoading.set(true);
|
||||||
this.store
|
this.store
|
||||||
.sendToTsp(this.invoiceId())
|
.returnFromSale(data, this.invoiceId())
|
||||||
.pipe(
|
.pipe(
|
||||||
finalize(() => {
|
finalize(() => {
|
||||||
this.backFromSaleLoading.set(false);
|
this.backFromSaleLoading.set(false);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import { Skeleton } from 'primeng/skeleton';
|
import { Skeleton } from 'primeng/skeleton';
|
||||||
import { Tag } from 'primeng/tag';
|
|
||||||
import images from 'src/assets/images';
|
import images from 'src/assets/images';
|
||||||
import { PosLandingStore } from '../../store/main.store';
|
import { PosLandingStore } from '../../store/main.store';
|
||||||
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
||||||
@@ -20,7 +19,7 @@ import { FavoriteCTAComponent } from './favorite-CTA.component';
|
|||||||
selector: 'pos-goods-list-view',
|
selector: 'pos-goods-list-view',
|
||||||
templateUrl: './list-view.component.html',
|
templateUrl: './list-view.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent, Tag],
|
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent],
|
||||||
})
|
})
|
||||||
export class PosGoodsListViewComponent {
|
export class PosGoodsListViewComponent {
|
||||||
private readonly store = inject(PosLandingStore);
|
private readonly store = inject(PosLandingStore);
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ export class PosLandingStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getGoods() {
|
private async getGoods() {
|
||||||
|
// if (this.state$().goods) return;
|
||||||
this.setState({ getGoodsLoading: true });
|
this.setState({ getGoodsLoading: true });
|
||||||
try {
|
try {
|
||||||
const res = await firstValueFrom(this.service.getGoods(this.state$().searchQuery));
|
const res = await firstValueFrom(this.service.getGoods(this.state$().searchQuery));
|
||||||
@@ -146,6 +147,7 @@ export class PosLandingStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getGoodCategories() {
|
private async getGoodCategories() {
|
||||||
|
// if (this.state$().goodCategories) return;
|
||||||
this.setState({ getGoodCategoriesLoading: true });
|
this.setState({ getGoodCategoriesLoading: true });
|
||||||
try {
|
try {
|
||||||
const res = await firstValueFrom(this.service.getGoodCategories());
|
const res = await firstValueFrom(this.service.getGoodCategories());
|
||||||
|
|||||||
@@ -243,9 +243,14 @@ export class InputComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isOutOfRange(numericValue: number): { min: boolean; max: boolean } {
|
private isOutOfRange(numericValue: number): { min: boolean; max: boolean } {
|
||||||
|
console.log('min', this.min, 'max', this.max, 'numericValue', numericValue);
|
||||||
|
|
||||||
|
const min = this.min !== undefined && this.min !== null ? this.min : Number.NEGATIVE_INFINITY;
|
||||||
|
const max = this.max !== undefined && this.max !== null ? this.max : Number.POSITIVE_INFINITY;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
min: !!((this.min || this.min === 0) && numericValue < this.min),
|
min: !!((min || min === 0) && numericValue < min),
|
||||||
max: !!((this.max || this.max === 0) && numericValue > this.max),
|
max: !!((max || max === 0) && numericValue > max),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,9 +277,12 @@ export class InputComponent {
|
|||||||
const controlValue = isIdentifierField ? value : value === '' ? '' : numericValue;
|
const controlValue = isIdentifierField ? value : value === '' ? '' : numericValue;
|
||||||
this.control.setValue(controlValue);
|
this.control.setValue(controlValue);
|
||||||
target.value = value;
|
target.value = value;
|
||||||
|
console.log('value', value, 'controlValue', controlValue, 'numericValue', numericValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
onInput($event: Event) {
|
onInput($event: Event) {
|
||||||
|
console.log('$event', $event);
|
||||||
|
|
||||||
const target = $event.target as HTMLInputElement | null;
|
const target = $event.target as HTMLInputElement | null;
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
@@ -288,11 +296,14 @@ export class InputComponent {
|
|||||||
value = this.normalizeLeadingZeros(value, allowDecimal);
|
value = this.normalizeLeadingZeros(value, allowDecimal);
|
||||||
|
|
||||||
value = this.enforceMaxLength(value);
|
value = this.enforceMaxLength(value);
|
||||||
|
console.log('$value', value);
|
||||||
let numericValue = this.parseNumericValue(value);
|
let numericValue = this.parseNumericValue(value);
|
||||||
|
|
||||||
const range = this.isOutOfRange(numericValue);
|
const range = this.isOutOfRange(numericValue);
|
||||||
|
console.log('$range', range);
|
||||||
if (value !== '' && (range.min || range.max)) {
|
if (value !== '' && (range.min || range.max)) {
|
||||||
|
console.log('in if');
|
||||||
|
|
||||||
this.notifyRangeError(range);
|
this.notifyRangeError(range);
|
||||||
value = this.lastValidNumericValue;
|
value = this.lastValidNumericValue;
|
||||||
numericValue = this.parseNumericValue(value);
|
numericValue = this.parseNumericValue(value);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<form [formGroup]="form" (ngSubmit)="submit()">
|
||||||
|
<field-invoice-date [control]="form.controls.invoice_date" />
|
||||||
|
|
||||||
|
@for (item of initialValues || []; track item.id) {
|
||||||
|
<div class="py-3">
|
||||||
|
@if ($index) {
|
||||||
|
<hr class="h-1 w-full" />
|
||||||
|
}
|
||||||
|
<p-divider align="right">
|
||||||
|
{{ $index + 1 }}- <b>{{ item.good_snapshot.name }}</b>
|
||||||
|
</p-divider>
|
||||||
|
@if (isGold(item)) {
|
||||||
|
<shared-gold-payload-form
|
||||||
|
[initialValues]="$any(mappedInitialItems[$index])"
|
||||||
|
[vatPercentage]="vatFromItem(item)"
|
||||||
|
[isCorrection]="true" />
|
||||||
|
} @else {
|
||||||
|
<shared-standard-payload-form
|
||||||
|
[initialValues]="$any(mappedInitialItems[$index])"
|
||||||
|
[measureUnit]="item.good_snapshot.measure_unit"
|
||||||
|
[vatPercentage]="vatFromItem(item)"
|
||||||
|
[isCorrection]="true" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<app-form-footer-actions />
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
import ISummary from '@/core/models/summary';
|
||||||
|
import { IPosOrderItem } from '@/domains/pos/modules/shop/models';
|
||||||
|
import { AbstractForm } from '@/shared/abstractClasses';
|
||||||
|
import {
|
||||||
|
SharedGoldPayloadFormComponent,
|
||||||
|
SharedStandardPayloadFormComponent,
|
||||||
|
} from '@/shared/components';
|
||||||
|
import { goldDiscountType } from '@/shared/localEnum/constants/goldDiscountType';
|
||||||
|
import { IGoldPayload, IStandardPayload } from '@/shared/models';
|
||||||
|
import { formatDate, GREGORIAN_DATE_FORMATS } from '@/utils';
|
||||||
|
import { Component, Input, QueryList, SimpleChanges, ViewChildren } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { Divider } from 'primeng/divider';
|
||||||
|
import { FieldInvoiceDateComponent } from '../../fields/invoice_date.component';
|
||||||
|
import { FormFooterActionsComponent } from '../../formFooterActions/form-footer-actions.component';
|
||||||
|
import { CorrectionInvoiceFormValue, TCorrectionItemPayload } from '../models';
|
||||||
|
import { IInvoiceItem } from '../sale-invoice-full-response.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'shared-correction-form',
|
||||||
|
templateUrl: 'form.component.html',
|
||||||
|
imports: [
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FieldInvoiceDateComponent,
|
||||||
|
FormFooterActionsComponent,
|
||||||
|
SharedGoldPayloadFormComponent,
|
||||||
|
SharedStandardPayloadFormComponent,
|
||||||
|
Divider,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SharedCorrectionFormComponent extends AbstractForm<
|
||||||
|
CorrectionInvoiceFormValue,
|
||||||
|
CorrectionInvoiceFormValue,
|
||||||
|
IInvoiceItem[]
|
||||||
|
> {
|
||||||
|
@Input({ required: true }) invoiceDate!: string;
|
||||||
|
|
||||||
|
@ViewChildren(SharedGoldPayloadFormComponent)
|
||||||
|
goldForms!: QueryList<SharedGoldPayloadFormComponent>;
|
||||||
|
@ViewChildren(SharedStandardPayloadFormComponent)
|
||||||
|
standardForms!: QueryList<SharedStandardPayloadFormComponent>;
|
||||||
|
|
||||||
|
form = this.fb.group({
|
||||||
|
invoice_date: [this.invoiceDate],
|
||||||
|
});
|
||||||
|
|
||||||
|
itemDrafts: Record<string, TCorrectionItemPayload> = {};
|
||||||
|
mappedInitialItems: TCorrectionItemPayload[] = [];
|
||||||
|
|
||||||
|
initForm() {
|
||||||
|
this.form.controls.invoice_date.setValue(
|
||||||
|
formatDate(this.invoiceDate, 'gregory', 'en', GREGORIAN_DATE_FORMATS.FULL)
|
||||||
|
);
|
||||||
|
this.itemDrafts = {};
|
||||||
|
this.mappedInitialItems = (this.initialValues || []).map((item) => this.mapToInitialItem(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
override ngOnInit(): void {
|
||||||
|
this.initForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
override ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes['initialValues'] || changes['invoiceDate']) {
|
||||||
|
this.initForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isGold(item: IInvoiceItem) {
|
||||||
|
return String(item.good_snapshot?.pricing_model || '').toUpperCase() === 'GOLD';
|
||||||
|
}
|
||||||
|
|
||||||
|
vatFromItem(item: IInvoiceItem) {
|
||||||
|
return Number(item.good_snapshot?.sku?.VAT || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapBaseItem(item: IInvoiceItem): Omit<TCorrectionItemPayload, 'payload'> {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
pricing_model: item.good_snapshot?.pricing_model || '',
|
||||||
|
good_id: item.good_id,
|
||||||
|
unit_price: Number(item.unit_price || 0),
|
||||||
|
quantity: Number(item.quantity || 0),
|
||||||
|
discount_amount: Number(item.discount || 0),
|
||||||
|
total_amount: Number(item.total_amount || 0),
|
||||||
|
tax_amount: 0,
|
||||||
|
base_total_amount: Number(item.unit_price || 0) * Number(item.quantity || 0),
|
||||||
|
measure_unit: (item.good_snapshot?.measure_unit || {}) as ISummary,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mapToGoldInitialItem(item: IInvoiceItem): IPosOrderItem<IGoldPayload> {
|
||||||
|
return {
|
||||||
|
...this.mapBaseItem(item),
|
||||||
|
payload: {
|
||||||
|
karat: (item.payload as any)?.karat,
|
||||||
|
wages: Number((item.payload as any)?.wages || 0),
|
||||||
|
commission: Number((item.payload as any)?.commission || 0),
|
||||||
|
profit: Number((item.payload as any)?.profit || 0),
|
||||||
|
discount_type: goldDiscountType.PROFIT,
|
||||||
|
} as IGoldPayload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mapToStandardInitialItem(item: IInvoiceItem): IPosOrderItem<IStandardPayload> {
|
||||||
|
return {
|
||||||
|
...this.mapBaseItem(item),
|
||||||
|
payload: {} as IStandardPayload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mapToInitialItem(item: IInvoiceItem): TCorrectionItemPayload {
|
||||||
|
if (this.isGold(item)) {
|
||||||
|
return this.mapToGoldInitialItem(item) as TCorrectionItemPayload;
|
||||||
|
}
|
||||||
|
return this.mapToStandardInitialItem(item) as TCorrectionItemPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
override submitForm() {
|
||||||
|
const goldQueue = [...(this.goldForms?.toArray() || [])];
|
||||||
|
const standardQueue = [...(this.standardForms?.toArray() || [])];
|
||||||
|
const items =
|
||||||
|
this.initialValues?.map((item, index) => {
|
||||||
|
const current = this.isGold(item)
|
||||||
|
? goldQueue.shift()?.getCorrectionValue()
|
||||||
|
: standardQueue.shift()?.getCorrectionValue();
|
||||||
|
const merged = (current || this.mappedInitialItems[index]) as TCorrectionItemPayload;
|
||||||
|
return {
|
||||||
|
...merged,
|
||||||
|
id: item.id,
|
||||||
|
pricing_model: item.good_snapshot?.pricing_model || '',
|
||||||
|
} as TCorrectionItemPayload;
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
console.log('items', items);
|
||||||
|
|
||||||
|
const hasChanges = (this.initialValues || []).some((item, index) => {
|
||||||
|
const source = this.mappedInitialItems[index];
|
||||||
|
const draft = items[index] || source;
|
||||||
|
return JSON.stringify(source) !== JSON.stringify(draft);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasChanges) {
|
||||||
|
this.toastService.warn({ text: 'هیچ تغییری در مقادیر ثبت نشده است.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onSubmit.emit({
|
||||||
|
invoice_date: this.form.controls.invoice_date.value || '',
|
||||||
|
items,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './form.component';
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { IPosOrderItem } from '@/domains/pos/modules/shop/models';
|
||||||
|
import { IGoldPayload, IStandardPayload } from '@/shared/models';
|
||||||
|
|
||||||
|
export type TCorrectionItemPayload = IPosOrderItem<IGoldPayload | IStandardPayload> & {
|
||||||
|
id: string;
|
||||||
|
pricing_model: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CorrectionInvoiceFormValue = {
|
||||||
|
invoice_date: string;
|
||||||
|
items: TCorrectionItemPayload[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ICorrectionRequest {
|
||||||
|
total_amount: number;
|
||||||
|
discount_amount: number;
|
||||||
|
tax_amount: number;
|
||||||
|
invoice_date: string;
|
||||||
|
items: IPosOrderItem[];
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './correction';
|
||||||
|
export * from './return';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export type ReturnFromSaleFormValue = {
|
||||||
|
items: {
|
||||||
|
id: string;
|
||||||
|
quantity: number;
|
||||||
|
maxQuantity: number;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
<form [formGroup]="form" (ngSubmit)="submit()">
|
<form [formGroup]="form" (ngSubmit)="submit()">
|
||||||
<p-message severity="info" icon="pi pi-info-circle">
|
<p-message severity="info">
|
||||||
در برگشت از فروش صورتحساب شما میتوانید صرفا تعداد محصولات و خدمات خود را کم و حذف کنید و حداقل مقدار محصولات و خدمات
|
<ol class="list-disc ps-4">
|
||||||
شما ۱ می باشد.
|
<li>تعداد محصولات/خدمات خود را کم و یا حذف کنید</li>
|
||||||
|
<li>حداقل ۱ خدمت/سرویس بایستی باقی بماند</li>
|
||||||
|
<li>تخفیفها، مالیات و قیمت نهایی تمام شده با توجه به مقادیر فاکتور اصلی به صورت سیستمی محاسبه خواهند شد.</li>
|
||||||
|
</ol>
|
||||||
</p-message>
|
</p-message>
|
||||||
|
|
||||||
<field-invoice-date [control]="form.controls.invoice_date" />
|
<field-invoice-date [control]="form.controls.invoice_date" />
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import { IInvoiceItem } from '../sale-invoice-full-response.model';
|
|||||||
import { SharedReturnFormItemComponent } from './item-form.component';
|
import { SharedReturnFormItemComponent } from './item-form.component';
|
||||||
|
|
||||||
type ItemForm = FormGroup<{
|
type ItemForm = FormGroup<{
|
||||||
|
id: FormControl<string | null>;
|
||||||
quantity: FormControl<number | null>;
|
quantity: FormControl<number | null>;
|
||||||
|
maxQuantity: FormControl<number | null>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
type BackFromSaleFormValue = {
|
type BackFromSaleFormValue = {
|
||||||
@@ -71,12 +73,15 @@ export class SharedReturnFormComponent extends AbstractForm<
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.initialValues?.forEach((item: any) => {
|
this.initialValues?.forEach((item: any) => {
|
||||||
|
const maxQuantity = Number(item.quantity || 0);
|
||||||
this.items.push(
|
this.items.push(
|
||||||
this.fb.group({
|
this.fb.group({
|
||||||
|
id: [item.id || null],
|
||||||
quantity: [
|
quantity: [
|
||||||
Number(item.quantity),
|
maxQuantity,
|
||||||
[Validators.required, Validators.min(0), Validators.max(Number(item.quantity))],
|
[Validators.required, Validators.min(0), Validators.max(maxQuantity)],
|
||||||
],
|
],
|
||||||
|
maxQuantity: [maxQuantity],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -93,16 +98,32 @@ export class SharedReturnFormComponent extends AbstractForm<
|
|||||||
}
|
}
|
||||||
|
|
||||||
override submitForm() {
|
override submitForm() {
|
||||||
const payload = this.items.controls.map((control) => ({
|
const payload = this.items.getRawValue().map((item) => ({
|
||||||
id: control.get('id')?.value,
|
id: item.id || '',
|
||||||
quantity: control.get('quantity')?.value,
|
quantity: Number(item.quantity || 0),
|
||||||
|
maxQuantity: Number(item.maxQuantity || 0),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const hasChanges = payload.some(
|
||||||
|
(item, index) => item.quantity !== Number(this.initialValues?.[index]?.quantity || 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasChanges) {
|
||||||
|
this.toastService.warn({
|
||||||
|
text: 'هیچ تغییری در مقادیر ثبت نشده است.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!payload.some((item) => item.quantity)) {
|
if (!payload.some((item) => item.quantity)) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
text: 'حداقل مقدار یکی از کالاها/خدمات باید بیشتر از ۰ باشد.',
|
text: 'حداقل مقدار یکی از کالاها/خدمات باید بیشتر از ۰ باشد.',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.onSubmit.emit({
|
||||||
|
items: payload.filter((item) => !!item.id),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export interface ISaleInvoiceFullRawResponse {
|
|||||||
main_id: Maybe<string>;
|
main_id: Maybe<string>;
|
||||||
tax_id: Maybe<string>;
|
tax_id: Maybe<string>;
|
||||||
reference_invoice: Maybe<string>;
|
reference_invoice: Maybe<string>;
|
||||||
|
refrence_by: Maybe<string>;
|
||||||
notes: Maybe<string>;
|
notes: Maybe<string>;
|
||||||
settlement_type: IEnumTranslate<InvoiceSettlementType>;
|
settlement_type: IEnumTranslate<InvoiceSettlementType>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,16 @@
|
|||||||
</app-card-data>
|
</app-card-data>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<shared-light-bottomsheet [(visible)]="showBackFromSaleForm" header=" برگشت از فروش">
|
<shared-light-bottomsheet [(visible)]="showReturnFromSaleForm" header=" برگشت از فروش">
|
||||||
<shared-return-form [initialValues]="invoice.items" [invoiceDate]="invoice.invoice_date" />
|
<shared-return-form
|
||||||
|
[initialValues]="invoice.items"
|
||||||
|
[invoiceDate]="invoice.invoice_date"
|
||||||
|
(onSubmit)="returnSubmit($event)" />
|
||||||
|
</shared-light-bottomsheet>
|
||||||
|
<shared-light-bottomsheet [(visible)]="showCorrectionForm" header="اصلاحی">
|
||||||
|
<shared-correction-form
|
||||||
|
[initialValues]="invoice.items"
|
||||||
|
[invoiceDate]="invoice.invoice_date"
|
||||||
|
(onSubmit)="correctionSubmit($event)" />
|
||||||
</shared-light-bottomsheet>
|
</shared-light-bottomsheet>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { Maybe } from '@/core';
|
|||||||
import { NativeBridgeService } from '@/core/services';
|
import { NativeBridgeService } from '@/core/services';
|
||||||
import { ToastService } from '@/core/services/toast.service';
|
import { ToastService } from '@/core/services/toast.service';
|
||||||
import { PosConfigPrintService } from '@/domains/pos/modules/configs/components/print/services/main.service';
|
import { PosConfigPrintService } from '@/domains/pos/modules/configs/components/print/services/main.service';
|
||||||
|
import { IPosReturnFromSaleRequest } from '@/domains/pos/modules/saleInvoices/models/returnFromSale';
|
||||||
|
import { IPosOrderItem } from '@/domains/pos/modules/shop/models';
|
||||||
import { PosInfoStore } from '@/domains/pos/store';
|
import { PosInfoStore } from '@/domains/pos/store';
|
||||||
import {
|
import {
|
||||||
CatalogInvoiceTypeTagComponent,
|
CatalogInvoiceTypeTagComponent,
|
||||||
@@ -44,6 +46,8 @@ import { ProgressSpinner } from 'primeng/progressspinner';
|
|||||||
import { TableModule } from 'primeng/table';
|
import { TableModule } from 'primeng/table';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { AppConfirmationService } from '../confirmationDialog/confirmation-dialog.service';
|
import { AppConfirmationService } from '../confirmationDialog/confirmation-dialog.service';
|
||||||
|
import { SharedCorrectionFormComponent } from './correctionForm';
|
||||||
|
import { CorrectionInvoiceFormValue, ICorrectionRequest, ReturnFromSaleFormValue } from './models';
|
||||||
import { SharedReturnFormComponent } from './returnForm/form.component';
|
import { SharedReturnFormComponent } from './returnForm/form.component';
|
||||||
|
|
||||||
export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
|
export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
|
||||||
@@ -68,6 +72,7 @@ export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
|
|||||||
SharedLightBottomsheetComponent,
|
SharedLightBottomsheetComponent,
|
||||||
ProgressSpinner,
|
ProgressSpinner,
|
||||||
SharedReturnFormComponent,
|
SharedReturnFormComponent,
|
||||||
|
SharedCorrectionFormComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SharedSaleInvoiceSingleViewComponent {
|
export class SharedSaleInvoiceSingleViewComponent {
|
||||||
@@ -90,18 +95,20 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
@Output() onSendToTsp = new EventEmitter<Observable<any>>();
|
@Output() onSendToTsp = new EventEmitter<Observable<any>>();
|
||||||
@Output() onResendToTsp = new EventEmitter<Observable<any>>();
|
@Output() onResendToTsp = new EventEmitter<Observable<any>>();
|
||||||
@Output() onInquiry = new EventEmitter<Observable<any>>();
|
@Output() onInquiry = new EventEmitter<Observable<any>>();
|
||||||
|
@Output() onCorrection = new EventEmitter<ICorrectionRequest>();
|
||||||
|
@Output() onReturnFromSale = new EventEmitter<IPosReturnFromSaleRequest>();
|
||||||
|
|
||||||
@ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef<any>;
|
@ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef<any>;
|
||||||
|
|
||||||
moreActionMenuItems = signal<MenuItem[]>([]);
|
moreActionMenuItems = signal<MenuItem[]>([]);
|
||||||
showCorrectionForm = signal(false);
|
showCorrectionForm = signal(false);
|
||||||
showBackFromSaleForm = signal(false);
|
showReturnFromSaleForm = signal(false);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.showErrors = this.showErrors.bind(this);
|
this.showErrors = this.showErrors.bind(this);
|
||||||
this.showRevokeConfirmation = this.showRevokeConfirmation.bind(this);
|
this.showRevokeConfirmation = this.showRevokeConfirmation.bind(this);
|
||||||
this.showCorrection = this.showCorrection.bind(this);
|
this.showCorrection = this.showCorrection.bind(this);
|
||||||
this.showBackFromSale = this.showBackFromSale.bind(this);
|
this.showReturnFromSale = this.showReturnFromSale.bind(this);
|
||||||
this.printInvoice = this.printInvoice.bind(this);
|
this.printInvoice = this.printInvoice.bind(this);
|
||||||
this.send = this.send.bind(this);
|
this.send = this.send.bind(this);
|
||||||
this.resend = this.resend.bind(this);
|
this.resend = this.resend.bind(this);
|
||||||
@@ -119,9 +126,15 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
send() {
|
send() {
|
||||||
this.onSendToTsp.emit();
|
this.onSendToTsp.emit();
|
||||||
}
|
}
|
||||||
|
sendCorrection(event: ICorrectionRequest) {
|
||||||
|
this.onCorrection.emit(event);
|
||||||
|
}
|
||||||
resend() {
|
resend() {
|
||||||
this.onResendToTsp.emit();
|
this.onResendToTsp.emit();
|
||||||
}
|
}
|
||||||
|
sendReturnFromSale(event: IPosReturnFromSaleRequest) {
|
||||||
|
this.onReturnFromSale.emit(event);
|
||||||
|
}
|
||||||
async showRevokeConfirmation() {
|
async showRevokeConfirmation() {
|
||||||
await this.confirmationService.ask({
|
await this.confirmationService.ask({
|
||||||
header: `ابطال صورتحساب شماره ${this.invoice!.invoice_number}`,
|
header: `ابطال صورتحساب شماره ${this.invoice!.invoice_number}`,
|
||||||
@@ -143,9 +156,11 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
showCorrection() {}
|
showCorrection() {
|
||||||
showBackFromSale() {
|
this.showCorrectionForm.set(true);
|
||||||
this.showBackFromSaleForm.set(true);
|
}
|
||||||
|
showReturnFromSale() {
|
||||||
|
this.showReturnFromSaleForm.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
@@ -192,7 +207,7 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
label: 'برگشت از فروش',
|
label: 'برگشت از فروش',
|
||||||
icon: 'pi pi-cart-minus',
|
icon: 'pi pi-cart-minus',
|
||||||
neededStatus: TspProviderResponseStatus.SUCCESS,
|
neededStatus: TspProviderResponseStatus.SUCCESS,
|
||||||
command: this.showBackFromSale,
|
command: this.showReturnFromSale,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'چاپ',
|
label: 'چاپ',
|
||||||
@@ -461,4 +476,70 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
refresh() {
|
refresh() {
|
||||||
this.onRefresh.emit();
|
this.onRefresh.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private mapCorrectionRequest(event: CorrectionInvoiceFormValue): ICorrectionRequest {
|
||||||
|
const items = (event.items || []).map(({ id, pricing_model, ...item }) => item);
|
||||||
|
|
||||||
|
const total_amount = items.reduce((sum, item) => sum + Number(item.total_amount || 0), 0);
|
||||||
|
const discount_amount = items.reduce((sum, item) => sum + Number(item.discount_amount || 0), 0);
|
||||||
|
const tax_amount = items.reduce((sum, item) => sum + Number(item.tax_amount || 0), 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
invoice_date: event.invoice_date,
|
||||||
|
items,
|
||||||
|
total_amount,
|
||||||
|
discount_amount,
|
||||||
|
tax_amount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
correctionSubmit(event: CorrectionInvoiceFormValue) {
|
||||||
|
this.sendCorrection(this.mapCorrectionRequest(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapReturnFromSaleRequest(event: ReturnFromSaleFormValue): IPosReturnFromSaleRequest {
|
||||||
|
const invoiceItems = this.invoice?.items || [];
|
||||||
|
const itemMap = new Map(invoiceItems.map((item) => [item.id, item]));
|
||||||
|
|
||||||
|
const items: IPosOrderItem[] = (event.items || [])
|
||||||
|
.map((item) => {
|
||||||
|
const source = itemMap.get(item.id);
|
||||||
|
if (!source) return null;
|
||||||
|
|
||||||
|
const quantity = Number(item.quantity || 0);
|
||||||
|
const unit_price = Number(source.unit_price || 0);
|
||||||
|
const discount_amount = Number(source.discount || 0);
|
||||||
|
const base_total_amount = unit_price * quantity;
|
||||||
|
const total_amount = Number(source.total_amount || 0);
|
||||||
|
const tax_amount = Number(source.payload?.wages || 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
good_id: source.good_id,
|
||||||
|
service_id: source.service_id || undefined,
|
||||||
|
quantity,
|
||||||
|
unit_price,
|
||||||
|
discount_amount,
|
||||||
|
base_total_amount,
|
||||||
|
total_amount,
|
||||||
|
tax_amount,
|
||||||
|
measure_unit: source.good_snapshot.measure_unit,
|
||||||
|
payload: source.payload as any,
|
||||||
|
notes: source.notes,
|
||||||
|
image_url: source.good_snapshot.image_url,
|
||||||
|
} as IPosOrderItem;
|
||||||
|
})
|
||||||
|
.filter((item): item is IPosOrderItem => !!item);
|
||||||
|
|
||||||
|
return {
|
||||||
|
invoice_date: this.invoice?.invoice_date || '',
|
||||||
|
items,
|
||||||
|
total_amount: items.reduce((sum, item) => sum + Number(item.total_amount || 0), 0),
|
||||||
|
discount_amount: items.reduce((sum, item) => sum + Number(item.discount_amount || 0), 0),
|
||||||
|
tax_amount: items.reduce((sum, item) => sum + Number(item.tax_amount || 0), 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
returnSubmit(event: ReturnFromSaleFormValue) {
|
||||||
|
this.sendReturnFromSale(this.mapReturnFromSaleRequest(event));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,8 @@
|
|||||||
[baseTotalAmount]="baseTotalAmount()"
|
[baseTotalAmount]="baseTotalAmount()"
|
||||||
[discountAmount]="form.controls.discount_amount.value || 0"
|
[discountAmount]="form.controls.discount_amount.value || 0"
|
||||||
[taxAmount]="taxAmount()" />
|
[taxAmount]="taxAmount()" />
|
||||||
<button pButton class="w-full sm:w-auto" (click)="submit()">{{ preparedCTAText() }}</button>
|
@if (!isCorrection) {
|
||||||
|
<button pButton class="w-full sm:w-auto" (click)="submit()">{{ preparedCTAText() }}</button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export class SharedGoldPayloadFormComponent extends AbstractForm<
|
|||||||
@Input({ required: true }) vatPercentage!: number;
|
@Input({ required: true }) vatPercentage!: number;
|
||||||
@Input() isRapidInvoice: boolean = false;
|
@Input() isRapidInvoice: boolean = false;
|
||||||
@Input() ctaText: string = '';
|
@Input() ctaText: string = '';
|
||||||
|
@Input() isCorrection: boolean = false;
|
||||||
|
|
||||||
@Output() onSubmitForm = new EventEmitter<IGoldPayload>();
|
@Output() onSubmitForm = new EventEmitter<IGoldPayload>();
|
||||||
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
||||||
@@ -124,8 +125,30 @@ export class SharedGoldPayloadFormComponent extends AbstractForm<
|
|||||||
|
|
||||||
form = this.initialForm();
|
form = this.initialForm();
|
||||||
|
|
||||||
|
getCorrectionValue(): IPosOrderItem<IGoldPayload> {
|
||||||
|
return this.prepareSubmitPayload({
|
||||||
|
...(this.initialValues as IPosOrderItem<IGoldPayload>),
|
||||||
|
unit_price: Number(this.form.controls.unit_price.value || 0),
|
||||||
|
quantity: Number(this.form.controls.quantity.value || 0),
|
||||||
|
discount_amount: Number(this.form.controls.discount_amount.value || 0),
|
||||||
|
payload: {
|
||||||
|
...((this.initialValues as IPosOrderItem<IGoldPayload>)?.payload || {}),
|
||||||
|
commission: Number(this.form.controls.payload.controls.commission.value || 0),
|
||||||
|
karat: this.form.controls.payload.controls.karat.value as TGoldKarat,
|
||||||
|
profit: Number(this.form.controls.payload.controls.profit.value || 0),
|
||||||
|
wages: Number(this.form.controls.payload.controls.wages.value || 0),
|
||||||
|
discount_type:
|
||||||
|
this.form.controls.payload.controls.discount_type.value || goldDiscountType.PROFIT,
|
||||||
|
},
|
||||||
|
} as IPosOrderItem<IGoldPayload>);
|
||||||
|
}
|
||||||
|
|
||||||
override submitForm(payload: IPosOrderItem) {
|
override submitForm(payload: IPosOrderItem) {
|
||||||
this.onSubmit.emit({
|
this.onSubmit.emit(this.prepareSubmitPayload(payload as IPosOrderItem<IGoldPayload>));
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareSubmitPayload(payload: IPosOrderItem<IGoldPayload>): IPosOrderItem<IGoldPayload> {
|
||||||
|
return {
|
||||||
...payload,
|
...payload,
|
||||||
total_amount: this.totalAmount(),
|
total_amount: this.totalAmount(),
|
||||||
base_total_amount: this.baseTotalAmount(),
|
base_total_amount: this.baseTotalAmount(),
|
||||||
@@ -139,7 +162,7 @@ export class SharedGoldPayloadFormComponent extends AbstractForm<
|
|||||||
discount_type:
|
discount_type:
|
||||||
this.form.controls.payload.controls.discount_type.value || goldDiscountType.PROFIT,
|
this.form.controls.payload.controls.discount_type.value || goldDiscountType.PROFIT,
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCalculateAmount(payload: Partial<IPosOrderItem<IGoldPayload>>) {
|
updateCalculateAmount(payload: Partial<IPosOrderItem<IGoldPayload>>) {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
[baseTotalAmount]="baseTotalAmount()"
|
[baseTotalAmount]="baseTotalAmount()"
|
||||||
[discountAmount]="discountAmount()"
|
[discountAmount]="discountAmount()"
|
||||||
[taxAmount]="taxAmount()" />
|
[taxAmount]="taxAmount()" />
|
||||||
<button pButton class="w-full sm:w-auto" (onClick)="submit()">{{ preparedCTAText() }}</button>
|
@if (!isCorrection) {
|
||||||
|
<button pButton class="w-full sm:w-auto" (onClick)="submit()">{{ preparedCTAText() }}</button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export class SharedStandardPayloadFormComponent extends AbstractForm<
|
|||||||
@Input({ required: true }) vatPercentage!: number;
|
@Input({ required: true }) vatPercentage!: number;
|
||||||
@Input() isRapidInvoice: boolean = false;
|
@Input() isRapidInvoice: boolean = false;
|
||||||
@Input() ctaText: string = '';
|
@Input() ctaText: string = '';
|
||||||
|
@Input() isCorrection: boolean = false;
|
||||||
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
||||||
|
|
||||||
private readonly initialForm = () => {
|
private readonly initialForm = () => {
|
||||||
@@ -55,14 +56,29 @@ export class SharedStandardPayloadFormComponent extends AbstractForm<
|
|||||||
};
|
};
|
||||||
|
|
||||||
form = this.initialForm();
|
form = this.initialForm();
|
||||||
override submitForm(payload: IPosOrderItem<IStandardPayload>) {
|
|
||||||
this.onSubmit.emit({
|
getCorrectionValue(): IPosOrderItem<IStandardPayload> {
|
||||||
|
return this.prepareSubmitPayload({
|
||||||
|
...(this.initialValues as IPosOrderItem<IStandardPayload>),
|
||||||
|
unit_price: Number(this.form.controls.unit_price.value || 0),
|
||||||
|
quantity: Number(this.form.controls.quantity.value || 0),
|
||||||
|
discount_amount: Number(this.form.controls.discount_amount.value || 0),
|
||||||
|
} as IPosOrderItem<IStandardPayload>);
|
||||||
|
}
|
||||||
|
|
||||||
|
override submitForm(payload: IPosOrderItem) {
|
||||||
|
this.onSubmit.emit(this.prepareSubmitPayload(payload as IPosOrderItem<IStandardPayload>));
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareSubmitPayload(payload: IPosOrderItem<IStandardPayload>): IPosOrderItem<IStandardPayload> {
|
||||||
|
return {
|
||||||
...payload,
|
...payload,
|
||||||
total_amount: this.totalAmount(),
|
total_amount: this.totalAmount(),
|
||||||
discount_amount: this.discountAmount(),
|
|
||||||
tax_amount: this.taxAmount(),
|
|
||||||
base_total_amount: this.baseTotalAmount(),
|
base_total_amount: this.baseTotalAmount(),
|
||||||
});
|
tax_amount: this.taxAmount(),
|
||||||
|
discount_amount: this.form.value.discount_amount || 0,
|
||||||
|
payload: {},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
baseTotalAmount = signal<number>(0);
|
baseTotalAmount = signal<number>(0);
|
||||||
@@ -88,5 +104,6 @@ export class SharedStandardPayloadFormComponent extends AbstractForm<
|
|||||||
this.discountAmount.set(discountAmount);
|
this.discountAmount.set(discountAmount);
|
||||||
this.taxAmount.set(taxAmount);
|
this.taxAmount.set(taxAmount);
|
||||||
this.totalAmount.set(baseTotalAmountWithoutTax + taxAmount);
|
this.totalAmount.set(baseTotalAmountWithoutTax + taxAmount);
|
||||||
|
this.onChangeTotalAmount.emit(this.totalAmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'bg-surface-ground font-bold': day.isCurrentDay,
|
'bg-surface-ground font-bold': day.isCurrentDay,
|
||||||
'bg-primary! text-primary-contrast!': isSelected(day),
|
'bg-primary! text-primary-contrast!': isSelected(day),
|
||||||
'text-muted-color! font-light': day.isDisabled,
|
'text-muted-color! font-light opacity-50': day.isDisabled,
|
||||||
}"
|
}"
|
||||||
(click)="selectDate(day)">
|
(click)="selectDate(day)">
|
||||||
{{ day.day }}
|
{{ day.day }}
|
||||||
|
|||||||
Reference in New Issue
Block a user