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:
@@ -2,7 +2,7 @@
|
||||
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
||||
<app-input label="نام" [control]="form.controls.name" name="name" />
|
||||
<app-input label="موقعیت" [control]="form.controls.location" name="location" />
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="">
|
||||
<uikit-field label="انبار">
|
||||
<p-select
|
||||
[options]="brands()"
|
||||
optionLabel="name"
|
||||
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 { IInventoryResponse } from '../../models';
|
||||
import { InventoriesService } from '../../services/main.service';
|
||||
|
||||
@Component({
|
||||
selector: 'inventories-select-field',
|
||||
templateUrl: './select.component.html',
|
||||
imports: [ReactiveFormsModule, Select, UikitFieldComponent],
|
||||
})
|
||||
export class InventoriesSelectComponent {
|
||||
@Input() control!: FormControl<Maybe<number>>;
|
||||
@Input() canInsert: boolean = false;
|
||||
|
||||
constructor(private service: InventoriesService) {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
loading = signal(false);
|
||||
brands = signal<Maybe<IInventoryResponse[]>>(null);
|
||||
|
||||
isOpenFormDialog = signal(false);
|
||||
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.service.getAll().subscribe({
|
||||
next: (res) => {
|
||||
this.brands.set(res.data);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
onOpenForm() {
|
||||
// this.isOpenFormDialog.set(true);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,80 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Maybe } from '@/core';
|
||||
import { ProductChargeFormComponent } from '@/modules/productCharges/components/form.component';
|
||||
import { KeyValueComponent } from '@/shared/components';
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { Button, ButtonDirective } from 'primeng/button';
|
||||
import { Card } from 'primeng/card';
|
||||
import { GalleriaModule } from 'primeng/galleria';
|
||||
import { IInventoryResponse } from '../models';
|
||||
import { InventoriesService } from '../services/main.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-inventory',
|
||||
selector: 'app-product',
|
||||
templateUrl: './single.component.html',
|
||||
imports: [
|
||||
ButtonDirective,
|
||||
RouterLink,
|
||||
GalleriaModule,
|
||||
KeyValueComponent,
|
||||
Card,
|
||||
Button,
|
||||
ProductChargeFormComponent,
|
||||
],
|
||||
})
|
||||
export class InventoryComponent {
|
||||
constructor() {}
|
||||
private route = inject(ActivatedRoute);
|
||||
constructor(private service: InventoriesService) {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
productId = this.route.snapshot.params['productId'];
|
||||
isOpenChargeForm = signal(false);
|
||||
loading = signal(false);
|
||||
data = signal<Maybe<IInventoryResponse>>(null);
|
||||
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.service.getSingle(this.productId).subscribe({
|
||||
next: (res) => {
|
||||
this.data.set(res);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
get topBarCardDetails() {
|
||||
return [
|
||||
// {
|
||||
// icon: 'pi pi-box',
|
||||
// label: 'موجودی',
|
||||
// value: this.data()? || 0,
|
||||
// },
|
||||
// {
|
||||
// icon: 'pi pi-dollar',
|
||||
// label: 'قیمت پایه',
|
||||
// value: this.data()?.basePrice?.toString() || 'تعیین نشده',
|
||||
// },
|
||||
// {
|
||||
// icon: 'pi pi-calendar',
|
||||
// label: 'تاریخ ایجاد',
|
||||
// value: this.data()?.createdAt
|
||||
// ? new Date(this.data()!.createdAt!).toLocaleDateString()
|
||||
// : '-',
|
||||
// },
|
||||
// {
|
||||
// icon: 'pi pi-clock',
|
||||
// label: 'تعداد فروش',
|
||||
// value: 1200,
|
||||
// },
|
||||
];
|
||||
}
|
||||
|
||||
openDeleteConfirm() {}
|
||||
openChargeForm() {
|
||||
this.isOpenChargeForm.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user