feat: Add Cardex module with filters and invoices components

- Implemented Cardex routes and integrated with the main application routing.
- Created filters component for Cardex to filter inventory and product data.
- Developed invoices component to display supplier invoices with detailed views.
- Enhanced Products module with pagination and state management using a store.
- Updated Suppliers module to include invoice management and detailed supplier views.
- Refactored state management in Products and Suppliers to utilize signals for reactive updates.
- Added new models and services to support the Cardex functionality.
- Improved UI components for better user experience in displaying data.
This commit is contained in:
2025-12-21 19:09:13 +03:30
parent 108d192f88
commit e937c994d7
29 changed files with 716 additions and 49 deletions
@@ -0,0 +1,11 @@
<form [formGroup]="form" class="flex items-end gap-4">
<div class="grow grid grid-cols-4 gap-2">
<inventories-select-field [control]="form.controls.inventoryId" />
<products-select-field [control]="form.controls.productId" />
<uikit-datepicker [control]="form.controls.startDate" />
<uikit-datepicker [control]="form.controls.endDate" />
</div>
<div class="shrink-0">
<button pButton>جستجو</button>
</div>
</form>
@@ -0,0 +1,31 @@
import { InventoriesSelectComponent } from '@/modules/inventories/components';
import { ProductsSelectComponent } from '@/modules/products/components/select/select.component';
import { UikitFlatpickrJalaliComponent } from '@/uikit';
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { ButtonDirective } from 'primeng/button';
@Component({
selector: 'cardex-filters',
templateUrl: './filters.component.html',
imports: [
ReactiveFormsModule,
InventoriesSelectComponent,
ProductsSelectComponent,
UikitFlatpickrJalaliComponent,
ButtonDirective,
],
})
export class CardexFiltersComponent {
private route = inject(ActivatedRoute);
private fb = inject(FormBuilder);
constructor() {}
form = this.fb.group({
inventoryId: [Number(this.route.snapshot.queryParams['inventoryId']) || 0],
productId: [Number(this.route.snapshot.queryParams['productId']) || 0],
startDate: [this.route.snapshot.queryParams['startDate'] || ''],
endDate: [this.route.snapshot.queryParams['endDate'] || ''],
});
}
@@ -0,0 +1 @@
export * from './filters/filters.component';
@@ -0,0 +1,5 @@
const baseUrl = '/api/v1/cardex';
export const CARDEX_API_ROUTES = {
cardex: () => `${baseUrl}`,
};
@@ -0,0 +1,2 @@
export * from './apiRoutes';
export * from './routes';
@@ -0,0 +1,17 @@
import { NamedRoutes } from '@/core';
import { Routes } from '@angular/router';
export type TCARDEXRouteNames = 'CARDEX';
export const cardexNamedRoutes: NamedRoutes<TCARDEXRouteNames> = {
CARDEX: {
path: 'cardex',
loadComponent: () => import('../../views/cardex.component').then((m) => m.CardexPageComponent),
meta: {
title: 'کاردکس',
pagePath: () => '/cardex',
},
},
};
export const CARDEX_ROUTES: Routes = Object.values(cardexNamedRoutes);
+2
View File
@@ -0,0 +1,2 @@
export * from './io';
export * from './types';
+66
View File
@@ -0,0 +1,66 @@
import {
IInventoryInfo,
IInventoryMovement,
IInventoryStockProduct,
IInventoryTransferRequestItem,
} from './types';
export interface IInventorySummaryRawResponse {
id: number;
name: string;
location: string;
isActive: boolean;
isPointOfSale: boolean;
createdAt: string;
updatedAt: string;
deletedAt: Maybe<string>;
}
export interface IInventorySummaryResponse extends IInventorySummaryRawResponse {}
export interface IInventoryDetailRawResponse extends IInventorySummaryRawResponse {
availableProductTypes: number;
availableProductCount: number;
availableProductsCost: number;
}
export interface IInventoryDetailResponse extends IInventoryDetailRawResponse {}
export interface IInventoryRequest {
name: string;
location: string;
isActive: boolean;
}
export interface IInventoryStockMovementRawResponse {
receiptId: string;
count: number;
info: IInventoryInfo;
movements: IInventoryMovement[];
}
export interface IInventoryStockMovementResponse extends IInventoryStockMovementRawResponse {}
export interface IInventoryTransferRequest {
code: string;
description: string;
fromInventoryId: number;
toInventoryId: number;
items: IInventoryTransferRequestItem[];
}
export interface IInventoryStockRawResponse {
quantity: number;
avgCost: number;
product: IInventoryStockProduct;
}
export interface IInventoryStockResponse extends IInventoryStockRawResponse {}
export interface IInventoryStockQuery {
isAvailable?: boolean;
}
export interface IInventoryProductCardexRawResponse extends IInventoryStockMovementRawResponse {}
export interface IInventoryProductCardexResponse extends IInventoryProductCardexRawResponse {}
+42
View File
@@ -0,0 +1,42 @@
import { IProductRawResponse } from '@/modules/products/models';
export interface IInventoryMovement {
id: number;
quantity: number;
fee: number;
totalCost: number;
avgCost: number;
product: IInventoryProduct;
remainedInStock: number;
}
export interface IInventoryProduct {
id: number;
name: string;
description: null;
sku: string;
barcode: string;
createdAt: string;
updatedAt: string;
deletedAt: null;
brandId: number;
categoryId: number;
}
export interface IInventoryInfo {
date: string;
type: string;
quantity: number;
fee: number;
totalCost: number;
referenceType: string;
referenceId: string;
createdAt: string;
}
export interface IInventoryTransferRequestItem {
productId: number;
count: number;
}
export interface IInventoryStockProduct extends IProductRawResponse {}
@@ -0,0 +1,8 @@
<div class="flex flex-col gap-4">
<p-card>
<cardex-filters />
</p-card>
<p-card>
<uikit-empty-state title="برای مشاهده‌ی کاردکس اطلاعات بالا را تکمیل کنید"> </uikit-empty-state>
</p-card>
</div>
@@ -0,0 +1,13 @@
import { UikitEmptyStateComponent } from '@/uikit';
import { Component } from '@angular/core';
import { Card } from 'primeng/card';
import { CardexFiltersComponent } from '../components';
@Component({
selector: 'app-cardex',
templateUrl: './cardex.component.html',
imports: [Card, CardexFiltersComponent, UikitEmptyStateComponent],
})
export class CardexPageComponent {
constructor() {}
}