feat: add sale invoice card component with functionality for sending and retrieving invoice status
- Implemented sale invoice card component with HTML and TypeScript files. - Added API routes for sale invoices including sending, retrying, and checking status. - Created models for sale invoice fiscal actions and filters. - Developed service for handling sale invoice operations. - Introduced store for managing sale invoice state. - Created views for listing and displaying single sale invoices. - Added support for managing stock keeping units with forms and services. - Implemented reusable select components for measure units and SKUs. - Enhanced shared components for input fields including fiscal ID, SKU type, and VAT.
This commit is contained in:
@@ -49,8 +49,6 @@ export abstract class AbstractForm<
|
||||
submitLoading = signal(false);
|
||||
|
||||
submit() {
|
||||
console.log('first');
|
||||
|
||||
this.form.markAllAsTouched();
|
||||
|
||||
if (this.form.valid) {
|
||||
|
||||
@@ -18,4 +18,5 @@ export const apiEnumTranslates = {
|
||||
consumerRole: 'نقش',
|
||||
unitType: 'واحد اندازهگیری',
|
||||
goodPricingModel: 'مدل محاسبه',
|
||||
fiscalResponseStatus: 'وضعیت ارسال به سامانه',
|
||||
} as Record<TEnumApi, string>;
|
||||
|
||||
@@ -19,4 +19,5 @@ export const ENUMS_API_ROUTES = {
|
||||
unitType: `${baseUrl}/unit_type`,
|
||||
goodPricingModel: `${baseUrl}/good_pricing_model`,
|
||||
consumerRole: `${baseUrl}/consumer_role`,
|
||||
fiscalResponseStatus: `${baseUrl}/fiscal_response_status`,
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
optionValue="value"
|
||||
[formControl]="control"
|
||||
[showClear]="showClear"
|
||||
[filter]="true"
|
||||
[name]="name || type"
|
||||
appendTo="body"
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,8 @@ const baseUrl = '/api/v1/catalog';
|
||||
export const CATALOG_API_ROUTES = {
|
||||
guilds: () => `${baseUrl}/guilds`,
|
||||
guildGoodCategories: (guildId: string) => `${baseUrl}/guilds/${guildId}/good_categories`,
|
||||
sku: () => `${baseUrl}/sku`,
|
||||
measureUnits: () => `${baseUrl}/measure_units`,
|
||||
providers: () => `${baseUrl}/providers`,
|
||||
devices: () => `${baseUrl}/devices`,
|
||||
deviceBrands: () => `${baseUrl}/device_brands`,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<uikit-field [label]="label" [control]="control" [showLabel]="showLabel" [showErrors]="showErrors">
|
||||
<p-select
|
||||
[loading]="loading()"
|
||||
[options]="items()"
|
||||
optionLabel="name"
|
||||
[optionValue]="selectOptionValue"
|
||||
placeholder="انتخاب واحد اندازهگیری"
|
||||
[formControl]="control"
|
||||
[showClear]="showClear"
|
||||
[name]="name || 'measure_unit_id'"
|
||||
[filter]="true"
|
||||
appendTo="body"
|
||||
/>
|
||||
</uikit-field>
|
||||
@@ -0,0 +1,31 @@
|
||||
import { IListingResponse } from '@/core/models/service.model';
|
||||
import ISummary from '@/core/models/summary';
|
||||
import { AbstractSelectComponent } from '@/shared/abstractClasses';
|
||||
import { UikitFieldComponent } from '@/uikit';
|
||||
import { Component, inject, Input } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { Select } from 'primeng/select';
|
||||
import { CatalogsService } from '../../service';
|
||||
|
||||
@Component({
|
||||
selector: 'catalog-measure-units-select',
|
||||
templateUrl: './select.component.html',
|
||||
imports: [UikitFieldComponent, Select, ReactiveFormsModule],
|
||||
})
|
||||
export class CatalogMeasureUnitsSelectComponent extends AbstractSelectComponent<
|
||||
ISummary,
|
||||
IListingResponse<ISummary>
|
||||
> {
|
||||
@Input() override showClear: boolean = false;
|
||||
@Input() label: string = '';
|
||||
@Input() name?: string = '';
|
||||
private readonly service = inject(CatalogsService);
|
||||
|
||||
ngOnInit() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
getDataService() {
|
||||
return this.service.getMeasureUnits();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './components/select.component';
|
||||
@@ -19,6 +19,18 @@ export class CatalogsService {
|
||||
return this.http.get<IListingResponse<ISummary>>(this.apiRoutes.guildGoodCategories(guildId));
|
||||
}
|
||||
|
||||
getSKU(search?: string): Observable<IListingResponse<ISummary>> {
|
||||
return this.http.get<IListingResponse<ISummary>>(this.apiRoutes.sku(), {
|
||||
params: {
|
||||
search: search || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getMeasureUnits(): Observable<IListingResponse<ISummary>> {
|
||||
return this.http.get<IListingResponse<ISummary>>(this.apiRoutes.measureUnits());
|
||||
}
|
||||
|
||||
getProviders(): Observable<IListingResponse<ISummary>> {
|
||||
return this.http.get<IListingResponse<ISummary>>(this.apiRoutes.providers());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<uikit-field [label]="label" [control]="control" [showLabel]="showLabel" [showErrors]="showErrors">
|
||||
<p-select
|
||||
[loading]="loading()"
|
||||
[options]="items()"
|
||||
optionLabel="name"
|
||||
[optionValue]="selectOptionValue"
|
||||
placeholder="انتخاب شناسهی کالا"
|
||||
[formControl]="control"
|
||||
[showClear]="showClear"
|
||||
[name]="name || 'sku_id'"
|
||||
[filter]="true"
|
||||
appendTo="body"
|
||||
/>
|
||||
</uikit-field>
|
||||
@@ -0,0 +1,31 @@
|
||||
import { IListingResponse } from '@/core/models/service.model';
|
||||
import ISummary from '@/core/models/summary';
|
||||
import { AbstractSelectComponent } from '@/shared/abstractClasses';
|
||||
import { UikitFieldComponent } from '@/uikit';
|
||||
import { Component, inject, Input } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { Select } from 'primeng/select';
|
||||
import { CatalogsService } from '../../service';
|
||||
|
||||
@Component({
|
||||
selector: 'catalog-sku-select',
|
||||
templateUrl: './select.component.html',
|
||||
imports: [UikitFieldComponent, Select, ReactiveFormsModule],
|
||||
})
|
||||
export class CatalogSkuSelectComponent extends AbstractSelectComponent<
|
||||
ISummary,
|
||||
IListingResponse<ISummary>
|
||||
> {
|
||||
@Input() override showClear: boolean = false;
|
||||
@Input() label: string = '';
|
||||
@Input() name?: string = '';
|
||||
private readonly service = inject(CatalogsService);
|
||||
|
||||
ngOnInit() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
getDataService() {
|
||||
return this.service.getSKU('');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './components/select.component';
|
||||
+8
-3
@@ -4,10 +4,15 @@ import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-fiscal-code',
|
||||
template: `<app-input label="کد مالیاتی" [control]="control" [name]="name" type="number" />`,
|
||||
template: `<app-input
|
||||
label="کد مالیاتی"
|
||||
[control]="control"
|
||||
[name]="name"
|
||||
[isLtrInput]="true"
|
||||
/>`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
export class FiscalCodeComponent {
|
||||
export class FiscalIdComponent {
|
||||
@Input({ required: true }) control = new FormControl<string>('');
|
||||
@Input() name = 'fiscal_code';
|
||||
@Input() name = 'fiscal_id';
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export * from './device_id.component';
|
||||
export * from './economic_code.component';
|
||||
export * from './email.component';
|
||||
export * from './first_name.component';
|
||||
export * from './fiscal_code.component';
|
||||
export * from './fiscal_id.component';
|
||||
export * from './guild_id.component';
|
||||
export * from './last_name.component';
|
||||
export * from './legal_name.component';
|
||||
@@ -29,6 +29,8 @@ export * from './registration_number.component';
|
||||
export * from './serial_number.component';
|
||||
export * from './set_off.component';
|
||||
export * from './sku.component';
|
||||
export * from './sku_type.component';
|
||||
export * from './terminal.component';
|
||||
export * from './unit_price.component';
|
||||
export * from './username.component';
|
||||
export * from './vat.component';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { CatalogSkuSelectComponent } from '@/shared/catalog/sku';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-sku',
|
||||
template: `<app-input label="شناسه عمومی" [control]="control" [name]="name" />`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
template: `<catalog-sku-select label="شناسهی کالا" [control]="control" [name]="name" />`,
|
||||
imports: [ReactiveFormsModule, CatalogSkuSelectComponent],
|
||||
})
|
||||
export class SkuComponent {
|
||||
@Input({ required: true }) control = new FormControl<string>('');
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-sku-type',
|
||||
template: `<app-input label="نوع" [control]="control" [name]="name" />`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
export class SkuTypeComponent {
|
||||
@Input({ required: true }) control = new FormControl<string>('');
|
||||
@Input() name = 'type';
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-vat',
|
||||
template: `<app-input label="مالیات بر ارزش افزوده" [control]="control" [name]="name" type="price" />`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
export class VatComponent {
|
||||
@Input({ required: true }) control = new FormControl<number | string>('');
|
||||
@Input() name = 'VAT';
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center p-4">
|
||||
<div class="grow">
|
||||
<div class="flex items-center gap-2">
|
||||
@if (backRoute) {
|
||||
<p-button icon="pi pi-arrow-right" aria-label="بازگشت" outlined [routerLink]="backRoute" />
|
||||
}
|
||||
<h4 class="m-0">{{ pageTitle }}</h4>
|
||||
<span class="text-xl font-bold">{{ pageTitle }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@if (actions) {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<div class="inline-flex flex-col gap-1 w-full">
|
||||
<div class="inline-flex gap-2 items-center w-full">
|
||||
<span class="text-muted-color text-base font-normal flex-auto grow-0">{{ label }}:</span>
|
||||
<div class="shrink-0 grow">
|
||||
<div
|
||||
class="shrink-0 grow"
|
||||
[ngClass]="{
|
||||
'text-right': alignment === 'start',
|
||||
'text-center': alignment === 'center',
|
||||
'text-left': alignment === 'end',
|
||||
}"
|
||||
>
|
||||
<ng-content>
|
||||
@if (valueType() === "badge") {
|
||||
<p-badge [value]="valueToShow()?.toString()" [severity]="value ? 'success' : 'danger'" />
|
||||
|
||||
@@ -17,6 +17,7 @@ export class KeyValueComponent {
|
||||
@Input() label!: string;
|
||||
@Input() value?: string | boolean | number;
|
||||
@Input() hint?: string;
|
||||
@Input() alignment?: 'start' | 'center' | 'end' = 'start';
|
||||
@Input() type:
|
||||
| 'price'
|
||||
| 'active'
|
||||
|
||||
@@ -8,13 +8,31 @@ type ControlConfig = [string, ValidatorFn[]];
|
||||
export const fieldControl = {
|
||||
name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
code: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
description: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []],
|
||||
company_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
first_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
firstName: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
last_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
description: (value = '', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
company_name: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
first_name: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
firstName: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
last_name: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
lastName: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
legal_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
legal_name: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
registration_code: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
@@ -52,7 +70,7 @@ export const fieldControl = {
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
fiscal_code: (value = '', isRequired = true): ControlConfig => [
|
||||
fiscal_id: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? [Validators.required, fiscalCodeValidator()] : [fiscalCodeValidator()],
|
||||
],
|
||||
@@ -73,31 +91,67 @@ export const fieldControl = {
|
||||
pos_type: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
type: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
role: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
unit_type: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
pricing_model: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
unit_type: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
pricing_model: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
gender: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []],
|
||||
legal: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []],
|
||||
individual: (value = '', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
partner_id: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
partner_id: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
brand_id: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
category_id: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
starts_at: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
expires_at: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
category_id: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
starts_at: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
expires_at: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
activated_expires_at: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
invoiceDate: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
invoiceDate: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
quantity: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
unit_price: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
unit_price: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
cash: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []],
|
||||
set_off: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []],
|
||||
setOff: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []],
|
||||
terminal: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []],
|
||||
terminal: (value = '', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
sku: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
VAT: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
is_public: (value = 'true', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
is_domestic: (value = 'true', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
model: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
serial_number: (value = '', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
|
||||
Reference in New Issue
Block a user