feat: rename 'fee' to 'unitPrice' across purchase components and models
- Updated form component to use 'unitPrice' instead of 'fee'. - Adjusted product charge row form fields and templates to reflect the new naming. - Modified receipt template and related components to utilize 'unitPrice'. - Changed models and interfaces to replace 'fee' with 'unitPrice'. - Updated supplier invoice and statistics components to align with the new naming convention. - Added new API routes for dashboard and statistics modules.
This commit is contained in:
+3
-1
@@ -3,6 +3,7 @@ import { BANK_ACCOUNTS_ROUTES } from '@/modules/bankAccounts/constants';
|
||||
import { BANK_BRANCHES_ROUTES } from '@/modules/bankBranches/constants';
|
||||
import { CARDEX_ROUTES } from '@/modules/cardex/constants';
|
||||
import { CUSTOMERS_ROUTES } from '@/modules/customers/constants';
|
||||
import { DASHBOARD_ROUTES } from '@/modules/dashboard/constants';
|
||||
import { INVENTORIES_ROUTES } from '@/modules/inventories/constants';
|
||||
import { POSComponent } from '@/modules/pos/views/pos.component';
|
||||
import { PRODUCT_BRANDS_ROUTES } from '@/modules/productBrands/constants';
|
||||
@@ -21,7 +22,8 @@ export const appRoutes: Routes = [
|
||||
path: '',
|
||||
component: AppLayout,
|
||||
children: [
|
||||
{ path: '', component: Dashboard },
|
||||
{ path: 'ng', component: Dashboard },
|
||||
...DASHBOARD_ROUTES,
|
||||
...USERS_ROUTES,
|
||||
...SUPPLIERS_ROUTES,
|
||||
...PRODUCT_BRANDS_ROUTES,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<app-page-data-list
|
||||
[pageTitle]="'مدیریت شعب بانک'"
|
||||
[addNewCtaLabel]="'افزودن شعبهی جدید'"
|
||||
[pageTitle]="'مدیریت حسابهای بانک'"
|
||||
[addNewCtaLabel]="'افزودن حساب جدید'"
|
||||
[columns]="columns"
|
||||
[showAdd]="true"
|
||||
emptyPlaceholderTitle="شعبهای یافت نشد"
|
||||
emptyPlaceholderDescription="برای افزودن شعبه جدید، روی دکمهٔ بالا کلیک کنید."
|
||||
emptyPlaceholderTitle="حسابی یافت نشد"
|
||||
emptyPlaceholderDescription="برای افزودن حساب جدید، روی دکمهٔ بالا کلیک کنید."
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
[showDetails]="true"
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
<div class=""></div>
|
||||
<app-page-data-list
|
||||
[pageTitle]="'تراکنشهای حسابهای بانک'"
|
||||
[columns]="columns"
|
||||
emptyPlaceholderTitle="تراکنشی یافت نشد"
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
[showDetails]="true"
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,48 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
IColumn,
|
||||
PageDataListComponent,
|
||||
} from '@/shared/components/pageDataList/page-data-list.component';
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { IBankAccountsResponse } from '../models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bank-account',
|
||||
templateUrl: './single.component.html',
|
||||
imports: [PageDataListComponent],
|
||||
})
|
||||
export class BankAccountComponent {
|
||||
constructor() {}
|
||||
|
||||
columns = [
|
||||
{ field: 'id', header: 'شناسه', width: '60px' },
|
||||
{
|
||||
field: 'name',
|
||||
header: 'نام',
|
||||
minWidth: '100px',
|
||||
},
|
||||
|
||||
{
|
||||
field: 'accountNumber',
|
||||
header: 'شماره حساب بانکی',
|
||||
canCopy: true,
|
||||
minWidth: '160px',
|
||||
},
|
||||
{
|
||||
field: 'iban',
|
||||
header: 'شماره شبا',
|
||||
canCopy: true,
|
||||
minWidth: '200px',
|
||||
},
|
||||
{
|
||||
field: 'cardNumber',
|
||||
header: 'شماره کارت بانکی',
|
||||
canCopy: true,
|
||||
minWidth: '160px',
|
||||
},
|
||||
{ field: 'createdAt', header: 'تاریخ ایجاد', type: 'date' },
|
||||
{ field: 'updatedAt', header: 'تاریخ بهروزرسانی', type: 'date' },
|
||||
] as IColumn[];
|
||||
|
||||
loading = signal(false);
|
||||
items = signal<IBankAccountsResponse[]>([]);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</td>
|
||||
<td class="text-center!">
|
||||
@if (item.type === "IN") {
|
||||
<span [appPriceMask]="item.fee"></span>
|
||||
<span [appPriceMask]="item.unitPrice"></span>
|
||||
} @else {
|
||||
0
|
||||
}
|
||||
@@ -68,13 +68,13 @@
|
||||
</td>
|
||||
<td class="text-center!">
|
||||
@if (item.type === "OUT") {
|
||||
<span [appPriceMask]="item.fee"></span>
|
||||
<span [appPriceMask]="item.unitPrice"></span>
|
||||
} @else {
|
||||
0
|
||||
}
|
||||
</td>
|
||||
<td class="text-center!">{{ item.remainedInStock }}</td>
|
||||
<!-- <td class="text-center!"><span [appPriceMask]="item.fee"></span></td>
|
||||
<!-- <td class="text-center!"><span [appPriceMask]="item.unitPrice"></span></td>
|
||||
<td class="text-center!"><span [appPriceMask]="item.totalCost"></span></td> -->
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ export interface ICardexRawResponse {
|
||||
id: number;
|
||||
type: string;
|
||||
quantity: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
totalCost: number;
|
||||
referenceType: string;
|
||||
referenceId: string;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
const baseUrl = '/api/v1/pos';
|
||||
|
||||
export const POS_API_ROUTES = {
|
||||
info: (posId: number) => `${baseUrl}/${posId}`,
|
||||
stock: (posId: number) => `${baseUrl}/${posId}/stock`,
|
||||
productCategories: (posId: number) => `${baseUrl}/${posId}/product-categories`,
|
||||
submitOrder: (posId: number) => `${baseUrl}/${posId}/orders/create`,
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './apiRoutes';
|
||||
export * from './routes';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NamedRoutes } from '@/core';
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const dashboardNamedRoutes: NamedRoutes<any> = {
|
||||
POS: {
|
||||
path: '',
|
||||
loadComponent: () =>
|
||||
import('../../views/dashboard.component').then((m) => m.DashboardComponent),
|
||||
meta: {
|
||||
title: 'داشبورد',
|
||||
pagePath: () => ``,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export type TDashboardRouteNames = keyof typeof dashboardNamedRoutes;
|
||||
|
||||
export const DASHBOARD_ROUTES: Routes = Object.values(dashboardNamedRoutes);
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<app-statistics-shared-top-alert-stocks class="block h-[300px]" />
|
||||
<app-statistics-shared-top-sales class="block h-[300px]" />
|
||||
<app-statistics-shared-top-supplier-debts class="block h-[300px]" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
import { StatisticsSharedTopAlertStocksComponent } from '@/modules/statistics/shared/components/top-alert-stocks.component';
|
||||
import { StatisticsSharedTopSalesComponent } from '@/modules/statistics/shared/components/top-sales.component';
|
||||
import { StatisticsSharedTopSupplierDebtsComponent } from '@/modules/statistics/shared/components/top-supplier-debts.component';
|
||||
import { Component } from '@angular/core';
|
||||
import images from 'src/assets/images';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
imports: [
|
||||
StatisticsSharedTopAlertStocksComponent,
|
||||
StatisticsSharedTopSalesComponent,
|
||||
StatisticsSharedTopSupplierDebtsComponent,
|
||||
],
|
||||
})
|
||||
export class DashboardComponent {
|
||||
placeholderLogo = images.placeholders.logo;
|
||||
|
||||
now = new Date();
|
||||
|
||||
// info = this.store.info;
|
||||
// infoLoading = this.store.getInfoLoading;
|
||||
|
||||
// getInfo() {
|
||||
// this.infoLoading.set(true);
|
||||
// this.service.getInfo(Number(this.posId)).subscribe({
|
||||
// next: (res) => {
|
||||
// this.info.set(res);
|
||||
// this.infoLoading.set(false);
|
||||
// },
|
||||
// error: () => {
|
||||
// this.infoLoading.set(false);
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
}
|
||||
@@ -74,10 +74,10 @@
|
||||
<p-sortIcon field="quantity" />
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="fee">
|
||||
<th pSortableColumn="unitPrice">
|
||||
<div class="flex items-center gap-2">
|
||||
قیمت واحد
|
||||
<p-sortIcon field="fee" />
|
||||
<p-sortIcon field="unitPrice" />
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="totalCost">
|
||||
@@ -94,7 +94,7 @@
|
||||
<td>{{ movement.product.id }}</td>
|
||||
<td>{{ movement.product.name }}</td>
|
||||
<td>{{ movement.quantity }}</td>
|
||||
<td><span [appPriceMask]="movement.fee"></span></td>
|
||||
<td><span [appPriceMask]="movement.unitPrice"></span></td>
|
||||
<td><span [appPriceMask]="movement.totalCost"></span></td>
|
||||
<td>
|
||||
<p-button type="button" icon="pi pi-search" [routerLink]="'/products/' + movement.product.id" />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[optionValue]="selectOptionValue"
|
||||
placeholder="انتخاب انبار"
|
||||
[formControl]="control"
|
||||
[showClear]="true"
|
||||
[showClear]="showClear"
|
||||
[filter]="true"
|
||||
appendTo="body"
|
||||
(onChange)="change($event.value)"
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IProductRawResponse } from '@/modules/products/models';
|
||||
export interface IInventoryMovement {
|
||||
id: number;
|
||||
quantity: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
totalCost: number;
|
||||
avgCost: number;
|
||||
product: IInventoryProduct;
|
||||
@@ -27,7 +27,7 @@ export interface IInventoryInfo {
|
||||
date: string;
|
||||
type: string;
|
||||
quantity: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
totalCost: number;
|
||||
referenceType: string;
|
||||
referenceId: string;
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
<p-sortIcon field="quantity" />
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="fee">
|
||||
<th pSortableColumn="unitPrice">
|
||||
<div class="flex items-center gap-2">
|
||||
قیمت متوسط
|
||||
<p-sortIcon field="fee" />
|
||||
<p-sortIcon field="unitPrice" />
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 4rem"></th>
|
||||
|
||||
@@ -80,10 +80,10 @@
|
||||
<p-sortIcon field="quantity" />
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="fee">
|
||||
<th pSortableColumn="unitPrice">
|
||||
<div class="flex items-center gap-2">
|
||||
قیمت متوسط
|
||||
<p-sortIcon field="fee" />
|
||||
<p-sortIcon field="unitPrice" />
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 4rem"></th>
|
||||
|
||||
@@ -48,16 +48,17 @@
|
||||
<div class="flex items-center gap-4 mt-auto">
|
||||
<p-inplace>
|
||||
<ng-template #display>
|
||||
<span class="font-bold text-lg shrink-0" [appPriceMask]="inOrderProduct.fee"></span>
|
||||
<span class="font-bold text-lg shrink-0" [appPriceMask]="inOrderProduct.unitPrice"></span>
|
||||
</ng-template>
|
||||
<ng-template #content let-closeCallback="closeCallback">
|
||||
<p-input-number
|
||||
type="text"
|
||||
inputStyleClass="w-24"
|
||||
[min]="0"
|
||||
[(ngModel)]="inOrderProduct.fee"
|
||||
[(ngModel)]="inOrderProduct.unitPrice"
|
||||
(onBlur)="
|
||||
updateInOrderProductFee(inOrderProduct.productId, $event, inOrderProduct.fee); closeCallback()
|
||||
updateInOrderProductFee(inOrderProduct.productId, $event, inOrderProduct.unitPrice);
|
||||
closeCallback()
|
||||
"
|
||||
/>
|
||||
</ng-template>
|
||||
|
||||
@@ -46,11 +46,11 @@ export class PosOrderCardComponent {
|
||||
}
|
||||
|
||||
updateInOrderProductFee(productId: number, $event: Event, prevFee: number) {
|
||||
const fee = $event.target
|
||||
const unitPrice = $event.target
|
||||
? parseFloat(($event.target as HTMLInputElement).ariaValueNow || prevFee.toString())
|
||||
: prevFee;
|
||||
|
||||
this.store.updateInOrderProductFee(productId, fee);
|
||||
this.store.updateInOrderProductFee(productId, unitPrice);
|
||||
}
|
||||
|
||||
clearOrderList() {
|
||||
|
||||
@@ -16,7 +16,7 @@ interface ICategorySummary {
|
||||
export interface IPosOrderItem {
|
||||
productId: number;
|
||||
count: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
}
|
||||
|
||||
export interface IPosInOrderProduct extends IPosOrderItem {
|
||||
|
||||
@@ -72,7 +72,7 @@ export class POSStore {
|
||||
|
||||
readonly orderPricingInfo = computed(() => {
|
||||
const totalAmount = this.inOrderProducts().reduce(
|
||||
(acc, curr) => acc + curr.fee * curr.count,
|
||||
(acc, curr) => acc + curr.unitPrice * curr.count,
|
||||
0,
|
||||
);
|
||||
const taxAmount = totalAmount * 0.1;
|
||||
@@ -188,7 +188,7 @@ export class POSStore {
|
||||
product,
|
||||
productId: product.id,
|
||||
count: 1,
|
||||
fee: parseFloat(product.salePrice),
|
||||
unitPrice: parseFloat(product.salePrice),
|
||||
maxQuantity: productStock.quantity,
|
||||
},
|
||||
],
|
||||
@@ -220,13 +220,13 @@ export class POSStore {
|
||||
}
|
||||
}
|
||||
|
||||
updateInOrderProductFee(productId: number, fee: number) {
|
||||
updateInOrderProductFee(productId: number, unitPrice: number) {
|
||||
const inOrderProducts = this.state$().inOrderProducts;
|
||||
|
||||
if (inOrderProducts.some((p) => p.productId === productId)) {
|
||||
const updatedProducts = inOrderProducts.map((p) => {
|
||||
if (p.productId === productId) {
|
||||
return { ...p, fee: fee };
|
||||
return { ...p, unitPrice: unitPrice };
|
||||
}
|
||||
return p;
|
||||
});
|
||||
@@ -255,7 +255,7 @@ export class POSStore {
|
||||
items: this.inOrderProducts()?.map((item) => ({
|
||||
productId: item.productId,
|
||||
count: item.count,
|
||||
fee: item.fee,
|
||||
unitPrice: item.unitPrice,
|
||||
})),
|
||||
};
|
||||
this.service.submitOrder(this.posId, orderPayload).subscribe();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<suppliers-select-field [control]="form.controls.supplierId" />
|
||||
<app-input label="تعداد" [control]="form.controls.count" />
|
||||
|
||||
<app-input label="قیمت اولیه" [control]="form.controls.fee" />
|
||||
<app-input label="قیمت اولیه" [control]="form.controls.unitPrice" />
|
||||
|
||||
<app-input label="مبلغ کل" [control]="form.controls.totalAmount" />
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export class ProductChargeFormComponent {
|
||||
|
||||
form = this.fb.group({
|
||||
count: [0, [Validators.required, Validators.min(1)]],
|
||||
fee: [0, [Validators.required, Validators.min(0)]],
|
||||
unitPrice: [0, [Validators.required, Validators.min(0)]],
|
||||
totalAmount: [0, [Validators.required, Validators.min(0)]],
|
||||
isSettled: [false],
|
||||
buyAt: ['', Validators.required],
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ export interface IProductChargeRaw {
|
||||
id: number;
|
||||
name: string;
|
||||
count: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
totalAmount: number;
|
||||
isSettled?: boolean;
|
||||
buyAt: string;
|
||||
@@ -20,7 +20,7 @@ export interface IProductChargeResponse extends IProductChargeRaw {}
|
||||
export interface IProductChargeRequest {
|
||||
name: string;
|
||||
count: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
totalAmount: number;
|
||||
isSettled?: boolean;
|
||||
buyAt: string;
|
||||
|
||||
@@ -24,7 +24,7 @@ export class ProductChargesComponent {
|
||||
{ field: 'id', header: 'شناسه' },
|
||||
{ field: 'name', header: 'نام' },
|
||||
{ field: 'count', header: 'تعداد' },
|
||||
{ field: 'fee', header: 'هزینه' },
|
||||
{ field: 'unitPrice', header: 'هزینه' },
|
||||
{ field: 'totalAmount', header: 'مبلغ کل' },
|
||||
{ field: 'isSettled', header: 'تسویه شده' },
|
||||
{ field: 'buyAt', header: 'تاریخ خرید' },
|
||||
|
||||
@@ -48,7 +48,7 @@ export class PurchaseFormComponent {
|
||||
this.fb.group({
|
||||
productId: [this.product?.id || 0, [Validators.required]],
|
||||
count: [0, [Validators.required, Validators.min(1)]],
|
||||
fee: [0, [Validators.required, Validators.min(0)]],
|
||||
unitPrice: [0, [Validators.required, Validators.min(0)]],
|
||||
}),
|
||||
]),
|
||||
inventoryId: [this.inventory?.id || 0, [Validators.required]],
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<products-select-field [control]="productIdControl" />
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<app-input label="تعداد" [control]="countControl" name="count" />
|
||||
<app-input label="قیمت" [control]="feeControl" name="fee" />
|
||||
<app-input label="قیمت" [control]="feeControl" name="unitPrice" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<product-charge-row-form-fields
|
||||
[productIdControl]="$any(product.get('productId'))"
|
||||
[countControl]="$any(product.get('count'))"
|
||||
[feeControl]="$any(product.get('fee'))"
|
||||
[feeControl]="$any(product.get('unitPrice'))"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export class ProductChargeRowFormComponent implements OnInit {
|
||||
FormGroup<{
|
||||
productId: FormControl<Maybe<number>>;
|
||||
count: FormControl<Maybe<number>>;
|
||||
fee: FormControl<Maybe<number>>;
|
||||
unitPrice: FormControl<Maybe<number>>;
|
||||
}>
|
||||
>;
|
||||
@Input() isSingleProduct = false;
|
||||
@@ -43,7 +43,7 @@ export class ProductChargeRowFormComponent implements OnInit {
|
||||
const productFormGroup = this.fb.group({
|
||||
productId: [0, [Validators.required]],
|
||||
count: [1, [Validators.required, Validators.min(1)]],
|
||||
fee: [0, [Validators.required, Validators.min(0)]],
|
||||
unitPrice: [0, [Validators.required, Validators.min(0)]],
|
||||
});
|
||||
this.productsControl.push(productFormGroup);
|
||||
}
|
||||
|
||||
+6
-6
@@ -28,27 +28,27 @@
|
||||
[autoResize]="false"
|
||||
></textarea>
|
||||
</td>
|
||||
<td><app-input [control]="purchaseItemControl.controls.fee" [showErrors]="false" /></td>
|
||||
<td><app-input [control]="purchaseItemControl.controls.unitPrice" [showErrors]="false" type="price" /></td>
|
||||
<td><app-input [control]="purchaseItemControl.controls.count" /></td>
|
||||
<td><span [appPriceMask]="purchaseItemControl.controls.total.value || 0"></span></td>
|
||||
<td>
|
||||
<!-- <td>
|
||||
<div class="flex items-center gap-1">
|
||||
<button pButton type="button" icon="pi pi-check" text size="small" (click)="toggleEditMode()"></button>
|
||||
</div>
|
||||
</td>
|
||||
</td> -->
|
||||
} @else {
|
||||
<td>{{ purchaseItemControl.controls.product.value?.sku || "" }}</td>
|
||||
<td>{{ purchaseItemControl.controls.product.value?.name || "" }}</td>
|
||||
<td>{{ purchaseItemControl.controls.description.value || "" }}</td>
|
||||
<td><span [appPriceMask]="purchaseItemControl.controls.fee.value || 0"></span></td>
|
||||
<td><span [appPriceMask]="purchaseItemControl.controls.unitPrice.value || 0"></span></td>
|
||||
<td>{{ purchaseItemControl.controls.count.value }}</td>
|
||||
<td><span [appPriceMask]="purchaseItemControl.controls.total.value || 0"></span></td>
|
||||
<td>
|
||||
<!-- <td>
|
||||
<div class="flex items-center gap-1">
|
||||
<button pButton type="button" icon="pi pi-pencil" text size="small" (click)="toggleEditMode()"></button>
|
||||
@if (canRemove) {
|
||||
<button pButton type="button" icon="pi pi-trash" text size="small" (click)="onDeleteClick()"></button>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
</td> -->
|
||||
}
|
||||
|
||||
+3
-3
@@ -34,12 +34,12 @@ export class PurchaseReceiptProductRowComponent {
|
||||
|
||||
ngOnInit() {
|
||||
this.purchaseItemControl?.controls.product.valueChanges.subscribe((res) => {
|
||||
this.purchaseItemControl?.controls.fee.setValue(Number(res?.salePrice) || 0);
|
||||
this.purchaseItemControl?.controls.unitPrice.setValue(Number(res?.salePrice) || 0);
|
||||
});
|
||||
this.purchaseItemControl?.controls.count.valueChanges.subscribe(() => {
|
||||
this.updateTotal();
|
||||
});
|
||||
this.purchaseItemControl?.controls.fee.valueChanges.subscribe(() => {
|
||||
this.purchaseItemControl?.controls.unitPrice.valueChanges.subscribe(() => {
|
||||
this.updateTotal();
|
||||
});
|
||||
}
|
||||
@@ -47,7 +47,7 @@ export class PurchaseReceiptProductRowComponent {
|
||||
updateTotal() {
|
||||
this.purchaseItemControl.controls.total.setValue(
|
||||
(this.purchaseItemControl.controls.count.value || 0) *
|
||||
(this.purchaseItemControl.controls.fee.value || 0),
|
||||
(this.purchaseItemControl.controls.unitPrice.value || 0),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+41
-23
@@ -9,38 +9,47 @@
|
||||
<span [class]="formIsSubmitted() && form.controls.inventory.invalid ? ' text-error' : 'text-muted-color'">
|
||||
انبار:
|
||||
</span>
|
||||
<shared-inline-edit>
|
||||
<ng-template #data>
|
||||
<p-inplace [disabled]="!!inventory">
|
||||
<ng-template #display>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">{{ form.controls.inventory.value?.name || "وارد نشده" }}</span>
|
||||
@if (!inventory) {
|
||||
<i class="pi pi-pencil text-sm text-muted-color cursor-pointer"></i>
|
||||
}
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #field>
|
||||
<ng-template #content let-closeCallback="closeCallback">
|
||||
<inventories-select-field
|
||||
[control]="form.controls.inventory"
|
||||
[isFullDataOptionValue]="true"
|
||||
[showErrors]="false"
|
||||
[showLabel]="false"
|
||||
[showClear]="false"
|
||||
/>
|
||||
</ng-template>
|
||||
</shared-inline-edit>
|
||||
</p-inplace>
|
||||
</div>
|
||||
<div class="w-full flex items-center gap-2 h-10">
|
||||
<span [class]="formIsSubmitted() && form.controls.supplier.invalid ? ' text-error' : 'text-muted-color'">
|
||||
تامین کننده:
|
||||
</span>
|
||||
<shared-inline-edit>
|
||||
<ng-template #data>
|
||||
<p-inplace>
|
||||
<ng-template #display>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">{{ form.controls.supplier.value?.fullname || "وارد نشده" }}</span>
|
||||
<i class="pi pi-pencil text-sm text-muted-color cursor-pointer"></i>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #field>
|
||||
<ng-template #content let-closeCallback="closeCallback">
|
||||
<suppliers-select-field
|
||||
[control]="form.controls.supplier"
|
||||
[isFullDataOptionValue]="true"
|
||||
[showErrors]="false"
|
||||
[showLabel]="false"
|
||||
[showClear]="false"
|
||||
/>
|
||||
</ng-template>
|
||||
</shared-inline-edit>
|
||||
</p-inplace>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
@@ -49,27 +58,33 @@
|
||||
<span [class]="formIsSubmitted() && form.controls.code.invalid ? ' text-error' : 'text-muted-color'">
|
||||
شماره رسید:
|
||||
</span>
|
||||
<shared-inline-edit>
|
||||
<ng-template #data>
|
||||
<p-inplace>
|
||||
<ng-template #display>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">{{ form.controls.code.value || "وارد نشده" }}</span>
|
||||
<i class="pi pi-pencil text-sm text-muted-color cursor-pointer"></i>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #field>
|
||||
<app-input [control]="form.controls.code" [showErrors]="false" />
|
||||
<ng-template #content let-closeCallback="closeCallback">
|
||||
<app-input [control]="form.controls.code" [showErrors]="false" (onBlur)="closeCallback()" />
|
||||
</ng-template>
|
||||
</shared-inline-edit>
|
||||
</p-inplace>
|
||||
</div>
|
||||
<div class="w-full flex items-center gap-2 h-10">
|
||||
<span [class]="formIsSubmitted() && form.controls.buyAt.invalid ? ' text-error' : 'text-muted-color'">
|
||||
تاریخ خرید:
|
||||
</span>
|
||||
<shared-inline-edit>
|
||||
<ng-template #data>
|
||||
<p-inplace>
|
||||
<ng-template #display>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">{{ form.controls.buyAt.value || "وارد نشده" }}</span>
|
||||
<i class="pi pi-pencil text-sm text-muted-color cursor-pointer"></i>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #field>
|
||||
<ng-template #content let-closeCallback="closeCallback">
|
||||
<uikit-datepicker [control]="form.controls.buyAt" [showErrors]="false" [showLabel]="false" />
|
||||
</ng-template>
|
||||
</shared-inline-edit>
|
||||
</p-inplace>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,7 +131,7 @@
|
||||
</ng-template>
|
||||
</shared-inline-edit>
|
||||
} @else if (col.field === "total") {
|
||||
{{ product["fee"] * product["count"] || 0 }}
|
||||
{{ product["unitPrice"] * product["count"] || 0 }}
|
||||
} @else if (col.field === "action") {
|
||||
<div class="flex items-center gap-2">
|
||||
<button></button>
|
||||
@@ -130,7 +145,7 @@
|
||||
</ng-template>
|
||||
<ng-template #footer>
|
||||
<tr>
|
||||
<td [attr.colspan]="8" class="p-0!">
|
||||
<td [attr.colspan]="7" class="p-0!">
|
||||
<div class="flex justify-center p-3">
|
||||
<button
|
||||
pButton
|
||||
@@ -143,7 +158,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td [attr.colspan]="6" class="border-e border-surface-700"></td>
|
||||
<td [attr.colspan]="5" class="border-e border-surface-700"></td>
|
||||
<td [attr.colspan]="2" class="p-0!">
|
||||
<div class="flex flex-col gap-4 w-full border-s border-surface-700 px-3 py-6">
|
||||
<div class="flex justify-between">
|
||||
@@ -158,7 +173,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td [attr.colspan]="6" class="p-0! border-none!">
|
||||
<td [attr.colspan]="5" class="p-0! border-none!">
|
||||
<div class="w-full flex items-center gap-2 py-10 border-surface-700 border-t-4">
|
||||
<span>مبلغ قابل پرداخت:</span>
|
||||
<span [appPriceAlphabet]="totalAmount + totalAmount * 0.1" class="font-bold text-lg"></span>
|
||||
@@ -180,6 +195,9 @@
|
||||
</div>
|
||||
|
||||
@if (purchaseReceiptResponse()) {
|
||||
<app-purchase-receipt-payment-wrapper [purchaseReceipt]="purchaseReceiptResponse()!" />
|
||||
<app-purchase-receipt-payment-wrapper
|
||||
[purchaseReceipt]="purchaseReceiptResponse()!"
|
||||
(onSubmit)="paymentSubmitted()"
|
||||
/>
|
||||
}
|
||||
</form>
|
||||
|
||||
+22
-13
@@ -5,7 +5,7 @@ import { IInventorySummaryResponse } from '@/modules/inventories/models';
|
||||
import { IProductResponse } from '@/modules/products/models';
|
||||
import { SuppliersSelectComponent } from '@/modules/suppliers/components/select/select.component';
|
||||
import { ISupplierResponse } from '@/modules/suppliers/models';
|
||||
import { InlineEditComponent, InputComponent } from '@/shared/components';
|
||||
import { InputComponent } from '@/shared/components';
|
||||
import { IColumn } from '@/shared/components/pageDataList/page-data-list.component';
|
||||
import { PurchaseReceiptPaymentWrapperComponent } from '@/shared/components/purchaseReceiptPayment/wrapper.component';
|
||||
import { PriceAlphabetDirective, PriceMaskDirective } from '@/shared/directives';
|
||||
@@ -16,6 +16,7 @@ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import dayjs from 'dayjs';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { Card } from 'primeng/card';
|
||||
import { Inplace } from 'primeng/inplace';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { IPurchaseReceiptResponse, IPurchaseRequest } from '../../models';
|
||||
import { PurchasesService } from '../../services/main.service';
|
||||
@@ -32,7 +33,6 @@ import { PurchaseReceiptProductRowComponent } from './purchase-receipt-product-r
|
||||
SuppliersSelectComponent,
|
||||
InputComponent,
|
||||
TableModule,
|
||||
InlineEditComponent,
|
||||
PurchaseReceiptProductRowComponent,
|
||||
PriceAlphabetDirective,
|
||||
PriceMaskDirective,
|
||||
@@ -41,6 +41,7 @@ import { PurchaseReceiptProductRowComponent } from './purchase-receipt-product-r
|
||||
ReactiveFormsModule,
|
||||
UikitFlatpickrJalaliComponent,
|
||||
PurchaseReceiptPaymentWrapperComponent,
|
||||
Inplace,
|
||||
],
|
||||
})
|
||||
export class PurchaseReceiptTemplateComponent {
|
||||
@@ -65,6 +66,7 @@ export class PurchaseReceiptTemplateComponent {
|
||||
field: 'name',
|
||||
header: 'نام کالا',
|
||||
minWidth: '160px',
|
||||
width: '160px',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
@@ -72,9 +74,9 @@ export class PurchaseReceiptTemplateComponent {
|
||||
width: '200px',
|
||||
},
|
||||
{
|
||||
field: 'fee',
|
||||
field: 'unitPrice',
|
||||
header: 'قیمت واحد',
|
||||
width: '120px',
|
||||
width: '200px',
|
||||
},
|
||||
{
|
||||
field: 'count',
|
||||
@@ -82,15 +84,16 @@ export class PurchaseReceiptTemplateComponent {
|
||||
width: '80px',
|
||||
},
|
||||
{
|
||||
field: 'total',
|
||||
field: 'totalPrice',
|
||||
header: 'مبلغ کل',
|
||||
type: 'price',
|
||||
width: '140px',
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
header: '',
|
||||
width: '100px',
|
||||
},
|
||||
// {
|
||||
// field: 'action',
|
||||
// header: '',
|
||||
// width: '100px',
|
||||
// },
|
||||
] as IColumn[];
|
||||
|
||||
form = this.fb.group({
|
||||
@@ -104,7 +107,7 @@ export class PurchaseReceiptTemplateComponent {
|
||||
this.fb.group({
|
||||
product: [null as Maybe<IProductResponse>, [Validators.required]],
|
||||
count: [0, [Validators.required, Validators.min(1)]],
|
||||
fee: [0, [Validators.required, Validators.min(0)]],
|
||||
unitPrice: [0, [Validators.required, Validators.min(0)]],
|
||||
description: [''],
|
||||
total: [0, [Validators.required, Validators.min(0)]],
|
||||
}),
|
||||
@@ -154,7 +157,7 @@ export class PurchaseReceiptTemplateComponent {
|
||||
{
|
||||
product: this.product,
|
||||
count: 0,
|
||||
fee: 0,
|
||||
unitPrice: 0,
|
||||
description: '',
|
||||
total: 0,
|
||||
},
|
||||
@@ -171,7 +174,7 @@ export class PurchaseReceiptTemplateComponent {
|
||||
this.fb.group({
|
||||
product: [null as Maybe<IProductResponse>, [Validators.required]],
|
||||
count: [0, [Validators.required, Validators.min(1)]],
|
||||
fee: [0, [Validators.required, Validators.min(0)]],
|
||||
unitPrice: [0, [Validators.required, Validators.min(0)]],
|
||||
description: [''],
|
||||
total: [0, [Validators.required, Validators.min(0)]],
|
||||
}),
|
||||
@@ -218,4 +221,10 @@ export class PurchaseReceiptTemplateComponent {
|
||||
}
|
||||
|
||||
onCancel() {}
|
||||
|
||||
paymentSubmitted() {
|
||||
this.purchaseReceiptResponse.set(null);
|
||||
this.form.reset();
|
||||
this.formIsSubmitted.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export * from './io';
|
||||
export interface IPurchaseItemFormGroup extends FormGroup<{
|
||||
product: FormControl<IProductResponse | null>;
|
||||
count: FormControl<number>;
|
||||
fee: FormControl<number>;
|
||||
unitPrice: FormControl<number>;
|
||||
description: FormControl<string>;
|
||||
total: FormControl<number>;
|
||||
}> {}
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@ export interface IPurchaseReceiptResponse extends IPurchaseReceiptRawResponse {}
|
||||
export interface IPurchaseItemRawResponse {
|
||||
id: number;
|
||||
count: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
total: number;
|
||||
productId: number;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export interface IPurchaseRequest {
|
||||
export interface IPurchaseItemRequest {
|
||||
productId: number;
|
||||
count: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface IPurchaseForm {
|
||||
export interface IPurchaseItemForm {
|
||||
product: Maybe<IProductResponse>;
|
||||
count: number;
|
||||
fee: number;
|
||||
unitPrice: number;
|
||||
total: number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
const baseUrl = '/api/v1/pos';
|
||||
|
||||
export const POS_API_ROUTES = {
|
||||
info: (posId: number) => `${baseUrl}/${posId}`,
|
||||
stock: (posId: number) => `${baseUrl}/${posId}/stock`,
|
||||
productCategories: (posId: number) => `${baseUrl}/${posId}/product-categories`,
|
||||
submitOrder: (posId: number) => `${baseUrl}/${posId}/orders/create`,
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './apiRoutes';
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="card p-0! h-full!">
|
||||
<div
|
||||
class="flex items-center gap-2 justify-between p-4 border-b border-surface-border bg-surface-card sticky top-0 z-10"
|
||||
>
|
||||
<span class="text-lg font-bold">{{ title }}</span>
|
||||
<button pButton icon="pi pi-refresh" outlined (click)="onRefresh.emit()" [loading]="loading"></button>
|
||||
</div>
|
||||
|
||||
<div class="p-4 h-full overflow-auto"><ng-content></ng-content></div>
|
||||
</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-statistics-shared-card',
|
||||
templateUrl: './card.component.html',
|
||||
imports: [ButtonDirective],
|
||||
host: {
|
||||
class: 'block h-full overflow-auto',
|
||||
},
|
||||
})
|
||||
export class StatisticsSharedCardComponent {
|
||||
@Input() title!: string;
|
||||
@Input() loading: boolean = false;
|
||||
@Output() onRefresh = new EventEmitter<void>();
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<app-statistics-shared-card title="لیست کالاهای رو به اتمام" [loading]="loading()" (onRefresh)="getData()">
|
||||
<app-page-data-list
|
||||
[columns]="column"
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
emptyPlaceholderDescription="در حال حاضر تمامی کالاها به مقدار کافی وجود دارند"
|
||||
>
|
||||
<ng-template #actionTpl let-item>
|
||||
<button pButton icon="pi pi-cart-plus" outlined [routerLink]="['/products', item.id, 'purchase']"></button>
|
||||
</ng-template>
|
||||
</app-page-data-list>
|
||||
</app-statistics-shared-card>
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
IColumn,
|
||||
PageDataListComponent,
|
||||
} from '@/shared/components/pageDataList/page-data-list.component';
|
||||
import { Component, signal, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { IStatisticsTopAlertStockResponse } from '../models/io';
|
||||
import { SharedStatisticsService } from '../services/main.service';
|
||||
import { StatisticsSharedCardComponent } from './card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-statistics-shared-top-alert-stocks',
|
||||
templateUrl: './top-alert-stocks.component.html',
|
||||
imports: [StatisticsSharedCardComponent, PageDataListComponent, ButtonDirective, RouterLink],
|
||||
})
|
||||
export class StatisticsSharedTopAlertStocksComponent {
|
||||
items = signal<IStatisticsTopAlertStockResponse[]>([]);
|
||||
loading = signal<boolean>(false);
|
||||
column = [] as IColumn[];
|
||||
@ViewChild('actionTpl', { static: true }) actionTpl!: TemplateRef<any>;
|
||||
|
||||
constructor(private readonly service: SharedStatisticsService) {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.column = [
|
||||
{ field: 'sku', header: 'شناسه' },
|
||||
{ field: 'name', header: 'عنوان' },
|
||||
{
|
||||
field: 'brand',
|
||||
header: 'برند',
|
||||
type: 'nested',
|
||||
nestedPath: 'name',
|
||||
},
|
||||
|
||||
{
|
||||
field: 'stock',
|
||||
header: 'موجودی ',
|
||||
},
|
||||
{ field: 'minimumStockAlertLevel', header: 'حد هشدار' },
|
||||
{
|
||||
field: 'chargeAction',
|
||||
header: '',
|
||||
customDataModel: this.actionTpl,
|
||||
width: '40px',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.service.getTopAlertStock().subscribe({
|
||||
next: (res) => {
|
||||
this.items.set(res.data);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
toPurchase(item: IStatisticsTopAlertStockResponse) {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<app-statistics-shared-card title="لیست پرفروشترین کالاها" [loading]="loading()" (onRefresh)="getData()">
|
||||
<app-page-data-list
|
||||
[columns]="column"
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
emptyPlaceholderDescription="در حال حاضر فروشی انجام نشده است."
|
||||
>
|
||||
</app-page-data-list>
|
||||
</app-statistics-shared-card>
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
IColumn,
|
||||
PageDataListComponent,
|
||||
} from '@/shared/components/pageDataList/page-data-list.component';
|
||||
import { Component, signal, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { IStatisticsTopSalesResponse } from '../models/io';
|
||||
import { SharedStatisticsService } from '../services/main.service';
|
||||
import { StatisticsSharedCardComponent } from './card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-statistics-shared-top-sales',
|
||||
templateUrl: './top-sales.component.html',
|
||||
imports: [StatisticsSharedCardComponent, PageDataListComponent, ButtonDirective, RouterLink],
|
||||
})
|
||||
export class StatisticsSharedTopSalesComponent {
|
||||
items = signal<IStatisticsTopSalesResponse[]>([]);
|
||||
loading = signal<boolean>(false);
|
||||
column = [] as IColumn[];
|
||||
@ViewChild('actionTpl', { static: true }) actionTpl!: TemplateRef<any>;
|
||||
|
||||
constructor(private readonly service: SharedStatisticsService) {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.column = [
|
||||
{ field: 'sku', header: 'شناسه' },
|
||||
{ field: 'name', header: 'عنوان' },
|
||||
{
|
||||
field: 'brand',
|
||||
header: 'برند',
|
||||
type: 'nested',
|
||||
nestedPath: 'name',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.service.getTopSales().subscribe({
|
||||
next: (res) => {
|
||||
this.items.set(res.data);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<app-statistics-shared-card title="لیست بدهی به تامینکنندگان" [loading]="loading()" (onRefresh)="getData()">
|
||||
<app-page-data-list
|
||||
[columns]="column"
|
||||
[items]="items()"
|
||||
[loading]="loading()"
|
||||
emptyPlaceholderDescription="در حال حاضر بدهی به تامینکنندگان ندارید."
|
||||
>
|
||||
</app-page-data-list>
|
||||
</app-statistics-shared-card>
|
||||
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
IColumn,
|
||||
PageDataListComponent,
|
||||
} from '@/shared/components/pageDataList/page-data-list.component';
|
||||
import { Component, signal, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { IStatisticsTopSupplierDebtsResponse } from '../models/io';
|
||||
import { SharedStatisticsService } from '../services/main.service';
|
||||
import { StatisticsSharedCardComponent } from './card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-statistics-shared-top-supplier-debts',
|
||||
templateUrl: './top-supplier-debts.component.html',
|
||||
imports: [StatisticsSharedCardComponent, PageDataListComponent],
|
||||
})
|
||||
export class StatisticsSharedTopSupplierDebtsComponent {
|
||||
items = signal<IStatisticsTopSupplierDebtsResponse[]>([]);
|
||||
loading = signal<boolean>(false);
|
||||
column = [] as IColumn[];
|
||||
@ViewChild('actionTpl', { static: true }) actionTpl!: TemplateRef<any>;
|
||||
|
||||
constructor(private readonly service: SharedStatisticsService) {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.column = [
|
||||
{ field: 'name', header: 'عنوان' },
|
||||
{
|
||||
field: 'totalDebt',
|
||||
header: 'بدهی کل',
|
||||
type: 'price',
|
||||
},
|
||||
{
|
||||
field: 'unpaidReceipts',
|
||||
header: 'فاکتورهای تسویهنشده',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getData() {
|
||||
this.loading.set(true);
|
||||
this.service.getTopSupplierDebts().subscribe({
|
||||
next: (res) => {
|
||||
this.items.set(res.data);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
const baseUrl = '/api/v1/statistics';
|
||||
|
||||
export const SHARED_STATISTICS_API_ROUTES = {
|
||||
topAlertStock: `${baseUrl}/top-alert-stocks`,
|
||||
topSales: `${baseUrl}/top-sales`,
|
||||
topSupplierDebts: `${baseUrl}/top-supplier-debts`,
|
||||
topSellProducts: `${baseUrl}/top-sell-products`,
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './apiRoutes';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './io';
|
||||
@@ -0,0 +1,27 @@
|
||||
import ISummary from '@/core/models/summary';
|
||||
|
||||
export interface IStatisticsTopAlertStockRawResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
sku: string;
|
||||
minimumStockAlertLevel: string;
|
||||
stockBalances: any[];
|
||||
category: ISummary;
|
||||
brand: ISummary;
|
||||
deficit: number;
|
||||
}
|
||||
export interface IStatisticsTopAlertStockResponse extends IStatisticsTopAlertStockRawResponse {}
|
||||
|
||||
export interface IStatisticsTopSalesRawResponse {}
|
||||
export interface IStatisticsTopSalesResponse extends IStatisticsTopSalesRawResponse {}
|
||||
|
||||
export interface IStatisticsTopSupplierDebtsRawResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
totalDebt: number;
|
||||
unpaidReceipts: number;
|
||||
}
|
||||
export interface IStatisticsTopSupplierDebtsResponse extends IStatisticsTopSupplierDebtsRawResponse {}
|
||||
|
||||
export interface IStatisticsTopSellProductsRawResponse {}
|
||||
export interface IStatisticsTopSellProductsResponse extends IStatisticsTopSellProductsRawResponse {}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { IPaginatedResponse } from '@/core/models/service.model';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SHARED_STATISTICS_API_ROUTES } from '../constants';
|
||||
import {
|
||||
IStatisticsTopAlertStockRawResponse,
|
||||
IStatisticsTopAlertStockResponse,
|
||||
IStatisticsTopSalesRawResponse,
|
||||
IStatisticsTopSalesResponse,
|
||||
IStatisticsTopSellProductsRawResponse,
|
||||
IStatisticsTopSellProductsResponse,
|
||||
IStatisticsTopSupplierDebtsRawResponse,
|
||||
IStatisticsTopSupplierDebtsResponse,
|
||||
} from '../models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SharedStatisticsService {
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
private apiRoutes = SHARED_STATISTICS_API_ROUTES;
|
||||
|
||||
getTopAlertStock(): Observable<IPaginatedResponse<IStatisticsTopAlertStockResponse>> {
|
||||
return this.http.get<IPaginatedResponse<IStatisticsTopAlertStockRawResponse>>(
|
||||
`${this.apiRoutes.topAlertStock}`,
|
||||
);
|
||||
}
|
||||
|
||||
getTopSales(): Observable<IPaginatedResponse<IStatisticsTopSalesResponse>> {
|
||||
return this.http.get<IPaginatedResponse<IStatisticsTopSalesRawResponse>>(
|
||||
`${this.apiRoutes.topSales}`,
|
||||
);
|
||||
}
|
||||
|
||||
getTopSupplierDebts(): Observable<IPaginatedResponse<IStatisticsTopSupplierDebtsResponse>> {
|
||||
return this.http.get<IPaginatedResponse<IStatisticsTopSupplierDebtsRawResponse>>(
|
||||
`${this.apiRoutes.topSupplierDebts}`,
|
||||
);
|
||||
}
|
||||
|
||||
getTopSellProducts(): Observable<IPaginatedResponse<IStatisticsTopSellProductsResponse>> {
|
||||
return this.http.get<IPaginatedResponse<IStatisticsTopSellProductsRawResponse>>(
|
||||
`${this.apiRoutes.topSellProducts}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -85,10 +85,10 @@
|
||||
<p-sortIcon field="quantity" />
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="fee">
|
||||
<th pSortableColumn="unitPrice">
|
||||
<div class="flex items-center gap-2">
|
||||
قیمت واحد
|
||||
<p-sortIcon field="fee" />
|
||||
<p-sortIcon field="unitPrice" />
|
||||
</div>
|
||||
</th>
|
||||
<th pSortableColumn="totalCost">
|
||||
@@ -105,7 +105,7 @@
|
||||
<td>{{ item.product.id }}</td>
|
||||
<td>{{ item.product.name }}</td>
|
||||
<td>{{ item.count }}</td>
|
||||
<td><span [appPriceMask]="item.fee"></span></td>
|
||||
<td><span [appPriceMask]="item.unitPrice"></span></td>
|
||||
<td><span [appPriceMask]="item.total"></span></td>
|
||||
<td>
|
||||
<p-button type="button" icon="pi pi-search" [routerLink]="'/products/' + item.product.id" />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[optionValue]="selectOptionValue"
|
||||
placeholder="انتخاب تامینکننده"
|
||||
[formControl]="control"
|
||||
[showClear]="true"
|
||||
[showClear]="showClear"
|
||||
[filter]="true"
|
||||
appendTo="body"
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface ISupplierInventorySummary {
|
||||
export interface ISupplierReceiptItemSummary {
|
||||
id: number;
|
||||
count: string;
|
||||
fee: string;
|
||||
unitPrice: string;
|
||||
total: string;
|
||||
product: {
|
||||
id: string;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</td>
|
||||
<td class="text-center!">
|
||||
@if (item.type === "IN") {
|
||||
<span [appPriceMask]="item.fee"></span>
|
||||
<span [appPriceMask]="item.unitPrice"></span>
|
||||
} @else {
|
||||
0
|
||||
}
|
||||
@@ -54,13 +54,13 @@
|
||||
</td>
|
||||
<td class="text-center!">
|
||||
@if (item.type === "OUT") {
|
||||
<span [appPriceMask]="item.fee"></span>
|
||||
<span [appPriceMask]="item.unitPrice"></span>
|
||||
} @else {
|
||||
0
|
||||
}
|
||||
</td>
|
||||
<td class="text-center!">{{ item.remainedInStock }}</td>
|
||||
<!-- <td class="text-center!"><span [appPriceMask]="item.fee"></span></td>
|
||||
<!-- <td class="text-center!"><span [appPriceMask]="item.unitPrice"></span></td>
|
||||
<td class="text-center!"><span [appPriceMask]="item.totalCost"></span></td> -->
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
[required]="isRequired"
|
||||
[invalid]="control.invalid && (control.touched || control.dirty)"
|
||||
(input)="onInput($event)"
|
||||
(onBlur)="blur.emit()"
|
||||
/>
|
||||
<p-inputgroup-addon>ریال</p-inputgroup-addon>
|
||||
</p-inputgroup>
|
||||
@@ -34,6 +35,7 @@
|
||||
[required]="isRequired"
|
||||
[invalid]="control.invalid && (control.touched || control.dirty)"
|
||||
(input)="onInput($event)"
|
||||
(onBlur)="blur.emit()"
|
||||
/>
|
||||
}
|
||||
@if (hint) {
|
||||
|
||||
@@ -45,6 +45,7 @@ export class InputComponent {
|
||||
@Input() showErrors = false;
|
||||
@Input() hint?: string;
|
||||
@Output() valueChange = new EventEmitter<string | number>();
|
||||
@Output() blur = new EventEmitter<void>();
|
||||
|
||||
onInput(ev: Event) {
|
||||
const v = (ev.target as HTMLInputElement).value;
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
[supplierId]="purchaseReceipt.supplierId.toString()"
|
||||
[inventoryId]="purchaseReceipt.inventoryId.toString()"
|
||||
[invoiceId]="purchaseReceipt.id.toString()"
|
||||
(onSubmit)="submitted()"
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ToastService } from '@/core/services/toast.service';
|
||||
import { SupplierInvoicePayFormComponent } from '@/modules/suppliers/components/invoices/pay-form.component';
|
||||
import { Component, Input, signal } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output, signal } from '@angular/core';
|
||||
import { IPurchaseReceiptResponse } from '../../../modules/purchases/models';
|
||||
import { ConfirmationDialogService } from '../confirmationDialog/confirmation-dialog.service';
|
||||
|
||||
@@ -12,6 +12,8 @@ import { ConfirmationDialogService } from '../confirmationDialog/confirmation-di
|
||||
export class PurchaseReceiptPaymentWrapperComponent {
|
||||
@Input() purchaseReceipt!: IPurchaseReceiptResponse;
|
||||
|
||||
@Output() onSubmit = new EventEmitter<void>();
|
||||
|
||||
showPaymentForm = signal(false);
|
||||
constructor(
|
||||
private confirmService: ConfirmationDialogService,
|
||||
@@ -40,7 +42,12 @@ export class PurchaseReceiptPaymentWrapperComponent {
|
||||
});
|
||||
}
|
||||
|
||||
submit() {}
|
||||
submitted() {
|
||||
console.log('submitted');
|
||||
|
||||
this.showPaymentForm.set(false);
|
||||
this.onSubmit.emit();
|
||||
}
|
||||
|
||||
toPaymentForm() {
|
||||
this.showPaymentForm.set(true);
|
||||
|
||||
Reference in New Issue
Block a user