feat(inventories): implement inventory movement list and transfer functionality

- Added InventoryMovementListComponent to display stock movements with filtering options.
- Created InventoriesTransferFormComponent for transferring stock between inventories.
- Implemented product cardex view to show detailed inventory movements for specific products.
- Enhanced product listing with loading states and improved UI components.
- Introduced Jalali date formatting directive for better date representation.
- Added utility functions for Jalali date conversions and comparisons.
- Created reusable details info card component for displaying inventory details.
- Updated movement reference types and tags for better categorization of inventory movements.
This commit is contained in:
2025-12-14 10:16:14 +03:30
parent b46b8b83f9
commit 35be7e0298
65 changed files with 1543 additions and 80 deletions
@@ -0,0 +1,65 @@
<p-table
stripedRows
[value]="items"
[loading]="loading"
showGridlines
tableStyleClass="table-fixed border-collapse"
scrollable
>
<ng-template #header>
<tr>
<th rowspan="2" [style]="{ textAlign: 'center', width: '4rem' }">ردیف</th>
<th rowspan="2" [style]="{ textAlign: 'center', width: '8rem' }">تاریخ</th>
<th rowspan="2" [style]="{ textAlign: 'center', width: '12rem' }">شرح</th>
<th rowspan="2" [style]="{ textAlign: 'center', width: '6rem' }">شماره</th>
<th rowspan="2" [style]="{ textAlign: 'center', minWidth: '14rem', width: '14rem' }">طرف حساب</th>
<th rowspan="2" [style]="{ textAlign: 'center', width: '12rem' }">انبار</th>
<th colspan="2" class="text-center!" [style]="{ width: '15rem' }">وارده</th>
<th colspan="2" class="text-center!" [style]="{ width: '15rem' }">خروجی</th>
<th colspan="3" class="text-center!" [style]="{ width: '25rem' }">مانده</th>
</tr>
<tr>
<th [style]="{ textAlign: 'center', width: '5rem' }">تعداد</th>
<th [style]="{ textAlign: 'center', width: '10rem' }">فی</th>
<th [style]="{ textAlign: 'center', width: '5rem' }">تعداد</th>
<th [style]="{ textAlign: 'center', width: '10rem' }">فی</th>
<th [style]="{ textAlign: 'center', width: '5rem' }">تعداد</th>
<th [style]="{ textAlign: 'center', width: '10rem' }">فی</th>
<th [style]="{ textAlign: 'center', width: '10rem' }">مجموع قیمت</th>
</tr>
</ng-template>
<ng-template #body let-item let-i="rowIndex">
<tr>
<td class="text-center!">{{ i + 1 }}</td>
<td class="text-center!" [jalaliDate]="item.createdAt"></td>
<td class="text-center!"><catalog-movement-reference-tag [type]="item.referenceType" /></td>
<td class="text-center!">{{ item.referenceId || "-" }}</td>
<td class="text-center!">{{ item.supplier?.name || "-" }}</td>
<td class="text-center!">{{ item.inventory.name || "-" }}</td>
<td class="text-center!">
{{ item.type === "IN" ? item.quantity : 0 }}
</td>
<td class="text-center!">
@if (item.type === "IN") {
<span [appPriceMask]="item.fee"></span>
} @else {
0
}
</td>
<td class="text-center!">
{{ item.type === "OUT" ? item.quantity : 0 }}
</td>
<td class="text-center!">
@if (item.type === "OUT") {
<span [appPriceMask]="item.fee"></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.totalCost"></span></td>
</tr>
</ng-template>
</p-table>
@@ -0,0 +1,22 @@
import { CatalogMovementReferenceTagComponent } from '@/shared/catalog/movementReferenceTypes';
import { JalaliDateDirective, PriceMaskDirective } from '@/shared/directives';
import { Component, Input } from '@angular/core';
import { TableModule } from 'primeng/table';
import { ICardexItem } from './type';
@Component({
selector: 'shared-cardex',
templateUrl: './cardex.component.html',
imports: [
TableModule,
JalaliDateDirective,
CatalogMovementReferenceTagComponent,
PriceMaskDirective,
],
hostDirectives: [PriceMaskDirective],
})
export class CardexComponent {
@Input() items: ICardexItem[] = [];
@Input() loading: boolean = false;
constructor() {}
}
+3
View File
@@ -0,0 +1,3 @@
import { IInventoryStockMovementResponse } from '@/modules/inventories/models';
export interface ICardexItem extends IInventoryStockMovementResponse {}
@@ -0,0 +1,11 @@
<p-card class="w-full border border-primary-700">
<div class="flex gap-5 text-primary-400">
<div class="shrink-0 pt-1">
<i [class]="'pi ' + icon + ' text-2xl'"></i>
</div>
<div class="grow flex flex-col gap-1">
<span class="text-sm">{{ label }}</span>
<span class="text-xl font-bold text-primary">{{ value }}</span>
</div>
</div>
</p-card>
@@ -0,0 +1,14 @@
import { Component, Input } from '@angular/core';
import { Card } from 'primeng/card';
@Component({
selector: 'details-info-card',
templateUrl: './details-info-card.component.html',
imports: [Card],
})
export class DetailsInfoCardComponent {
@Input() icon: string = '';
@Input() label: string = '';
@Input() value: string = '';
constructor() {}
}
@@ -1,4 +1,5 @@
import { PaginatorComponent, UikitCopyComponent, UikitEmptyStateComponent } from '@/uikit';
import { formatJalali } from '@/utils';
import { CommonModule } from '@angular/common';
import {
Component,
@@ -124,8 +125,7 @@ export class PageDataListComponent<I> {
switch (column.type) {
case 'date':
if (!data) return '-';
const date = new Date(data);
return isNaN(date.getTime()) ? '-' : date.toLocaleDateString();
return formatJalali(data);
case 'boolean':
return data ? 'بله' : 'خیر';
case 'price':