feat(purchases): implement purchase receipt functionality with form and receipt template
- Added `ProductPurchaseComponent` to handle product purchases. - Created `PurchaseReceiptTemplateComponent` for displaying purchase receipt details. - Developed `PurchaseFormComponent` for managing purchase form inputs and submission. - Introduced `ProductChargeRowFormComponent` and `ProductChargeRowFormFieldsComponent` for handling product charge rows in the form. - Implemented `PurchaseReceiptProductRowComponent` for displaying individual product rows in the receipt. - Established API routes for purchase operations in `apiRoutes`. - Integrated suppliers and inventories selection components. - Added utility functions for converting prices to Persian alphabet. - Created a utility for generating full names from name parts. - Enhanced form validation and submission handling in the purchase form. - Updated styles and templates for improved UI/UX.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<app-input label="شهر" [control]="form.controls.city" name="city" />
|
||||
<app-input label="استان" [control]="form.controls.state" name="state" />
|
||||
<app-input label="کشور" [control]="form.controls.country" name="country" />
|
||||
<app-input label="فعال" [control]="form.controls.isActive" name="isActive" type="switch" />
|
||||
<app-input label="وضعیت" [control]="form.controls.isActive" name="isActive" type="switch" />
|
||||
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="form.disabled" (onCancel)="close()" />
|
||||
</form>
|
||||
</p-dialog>
|
||||
|
||||
@@ -41,13 +41,13 @@ export class SupplierFormComponent {
|
||||
form = this.fb.group({
|
||||
firstName: [this.initialValues?.firstName || null, [Validators.required]],
|
||||
lastName: [this.initialValues?.lastName || null, [Validators.required]],
|
||||
email: [this.initialValues?.email || null, [Validators.required, Validators.email]],
|
||||
mobileNumber: [this.initialValues?.mobileNumber || null, [Validators.required]],
|
||||
address: [this.initialValues?.address || null, [Validators.required]],
|
||||
city: [this.initialValues?.city || null, [Validators.required]],
|
||||
state: [this.initialValues?.state || null, [Validators.required]],
|
||||
country: [this.initialValues?.country || null, [Validators.required]],
|
||||
isActive: [this.initialValues?.isActive || false, [Validators.required]],
|
||||
email: [this.initialValues?.email || null, [Validators.email]],
|
||||
address: [this.initialValues?.address || null],
|
||||
city: [this.initialValues?.city || null],
|
||||
state: [this.initialValues?.state || null],
|
||||
country: [this.initialValues?.country || null],
|
||||
isActive: [this.initialValues?.isActive || false],
|
||||
});
|
||||
|
||||
submitLoading = signal(false);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="">
|
||||
<uikit-field label="تامینکننده">
|
||||
<p-select
|
||||
[options]="items()"
|
||||
optionLabel="fullname"
|
||||
optionValue="id"
|
||||
placeholder="انتخاب تامینکننده"
|
||||
[formControl]="control"
|
||||
[showClear]="true"
|
||||
[filter]="true"
|
||||
appendTo="body"
|
||||
>
|
||||
</p-select>
|
||||
</uikit-field>
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { UikitFieldComponent } from '@/uikit';
|
||||
import { Component, Input, signal } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { Select } from 'primeng/select';
|
||||
import { ISupplierResponse } from '../../models';
|
||||
import { SuppliersService } from '../../services/main.service';
|
||||
|
||||
@Component({
|
||||
selector: 'suppliers-select-field',
|
||||
templateUrl: './select.component.html',
|
||||
imports: [ReactiveFormsModule, Select, UikitFieldComponent],
|
||||
})
|
||||
export class SuppliersSelectComponent {
|
||||
@Input() control!: FormControl<Maybe<number>>;
|
||||
@Input() canInsert: boolean = false;
|
||||
|
||||
constructor(private service: SuppliersService) {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
loading = signal(false);
|
||||
items = signal<Maybe<ISupplierResponse[]>>(null);
|
||||
|
||||
isOpenFormDialog = signal(false);
|
||||
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.service.getAll().subscribe({
|
||||
next: (res) => {
|
||||
this.items.set(res.data);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
onOpenForm() {
|
||||
// this.isOpenFormDialog.set(true);
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -14,7 +14,9 @@ export interface ISupplierRawResponse {
|
||||
deletedAt: Date;
|
||||
}
|
||||
|
||||
export interface ISupplierResponse extends ISupplierRawResponse {}
|
||||
export interface ISupplierResponse extends ISupplierRawResponse {
|
||||
fullname: string;
|
||||
}
|
||||
|
||||
export interface ISupplierRequest {
|
||||
firstName: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IPaginatedResponse } from '@/core/models/service.model';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { SUPPLIERS_API_ROUTES } from '../constants';
|
||||
import { ISupplierRawResponse, ISupplierRequest, ISupplierResponse } from '../models';
|
||||
|
||||
@@ -12,14 +12,32 @@ export class SuppliersService {
|
||||
private apiRoutes = SUPPLIERS_API_ROUTES;
|
||||
|
||||
getAll(): Observable<IPaginatedResponse<ISupplierResponse>> {
|
||||
return this.http.get<IPaginatedResponse<ISupplierRawResponse>>(this.apiRoutes.list());
|
||||
return this.http.get<IPaginatedResponse<ISupplierRawResponse>>(this.apiRoutes.list()).pipe(
|
||||
map((res) => ({
|
||||
...res,
|
||||
data: res.data.map((item) => ({
|
||||
...item,
|
||||
fullname: `${item.firstName} ${item.lastName}`,
|
||||
})),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
getSingle(supplierId: string): Observable<ISupplierResponse> {
|
||||
return this.http.get<ISupplierRawResponse>(this.apiRoutes.single(supplierId));
|
||||
return this.http.get<ISupplierRawResponse>(this.apiRoutes.single(supplierId)).pipe(
|
||||
map((item) => ({
|
||||
...item,
|
||||
fullname: `${item.firstName} ${item.lastName}`,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
create(data: ISupplierRequest): Observable<ISupplierResponse> {
|
||||
return this.http.post<ISupplierRawResponse>(this.apiRoutes.list(), data);
|
||||
return this.http.post<ISupplierRawResponse>(this.apiRoutes.list(), data).pipe(
|
||||
map((item) => ({
|
||||
...item,
|
||||
fullname: `${item.firstName} ${item.lastName}`,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,24 @@ export class SuppliersComponent {
|
||||
}
|
||||
|
||||
columns = [
|
||||
{ field: 'id', header: 'شناسه' },
|
||||
{ field: 'firstName', header: 'نام' },
|
||||
{ field: 'lastName', header: 'نام خانوادگی' },
|
||||
{ field: 'email', header: 'ایمیل' },
|
||||
{
|
||||
field: 'name',
|
||||
header: 'نام',
|
||||
customDataModel(item) {
|
||||
return `${item.firstName} ${item.lastName}` || '-';
|
||||
},
|
||||
},
|
||||
{ field: 'mobileNumber', header: 'شماره موبایل' },
|
||||
{ field: 'address', header: 'آدرس' },
|
||||
{ field: 'city', header: 'شهر' },
|
||||
{ field: 'state', header: 'استان' },
|
||||
{ field: 'country', header: 'کشور' },
|
||||
{ field: 'isActive', header: 'فعال' },
|
||||
{ field: 'createdAt', header: 'تاریخ ایجاد' },
|
||||
{ field: 'updatedAt', header: 'تاریخ بهروزرسانی' },
|
||||
{
|
||||
field: 'isActive',
|
||||
header: 'وضعیت',
|
||||
customDataModel(item) {
|
||||
return item.isActive ? 'فعال' : 'غیرفعال';
|
||||
},
|
||||
},
|
||||
{ field: 'createdAt', header: 'تاریخ ایجاد', type: 'date' },
|
||||
{ field: 'updatedAt', header: 'تاریخ بهروزرسانی', type: 'date' },
|
||||
] as IColumn[];
|
||||
|
||||
loading = signal(false);
|
||||
|
||||
Reference in New Issue
Block a user