fix ui issues and upload image for guild good
This commit is contained in:
@@ -31,7 +31,7 @@
|
|||||||
<app-key-value label="کسب و کار" [value]="invoice()?.pos!.complex.business_activity.name" />
|
<app-key-value label="کسب و کار" [value]="invoice()?.pos!.complex.business_activity.name" />
|
||||||
<app-key-value label="مجموعه" [value]="invoice()?.pos!.complex.name" />
|
<app-key-value label="مجموعه" [value]="invoice()?.pos!.complex.name" />
|
||||||
<app-key-value label="پایانهی فروش" [value]="invoice()?.pos!.name" />
|
<app-key-value label="پایانهی فروش" [value]="invoice()?.pos!.name" />
|
||||||
<app-key-value label="ایجاد کننده" [value]="invoice()?.account?.account?.username" />
|
<app-key-value label="ایجاد کننده" [value]="invoice()?.consumer_account?.account?.username" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (variant !== "customer") {
|
@if (variant !== "customer") {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog';
|
import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog';
|
||||||
import { KeyValueComponent } from '@/shared/components';
|
import { KeyValueComponent } from '@/shared/components';
|
||||||
import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus';
|
import { getLicenseStatus } from '@/utils';
|
||||||
import { Component, computed, inject } from '@angular/core';
|
import { Component, computed, inject } from '@angular/core';
|
||||||
import { Dialog } from 'primeng/dialog';
|
import { Dialog } from 'primeng/dialog';
|
||||||
import { Message } from 'primeng/message';
|
import { Message } from 'primeng/message';
|
||||||
@@ -14,15 +14,7 @@ import { ConsumerStore } from '../store/main.store';
|
|||||||
export class ConsumerLicenseInfoDialogComponent extends AbstractDialog {
|
export class ConsumerLicenseInfoDialogComponent extends AbstractDialog {
|
||||||
private readonly store = inject(ConsumerStore);
|
private readonly store = inject(ConsumerStore);
|
||||||
|
|
||||||
license = computed(() => this.store.entity()?.license);
|
license = computed(() => this.store.entity()?.license_info);
|
||||||
|
|
||||||
licenseStatus = computed(() => {
|
licenseStatus = computed(() => getLicenseStatus(getLicenseStatus(this.license()?.expires_at)));
|
||||||
if (!this.store.entity()?.license?.expires_at) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (new Date().getTime() > new Date(this.store.entity()?.license?.expires_at || '').getTime()) {
|
|
||||||
return licenseStatus.EXPIRED;
|
|
||||||
}
|
|
||||||
return licenseStatus.ACTIVE;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LayoutService } from '@/layout/service/layout.service';
|
import { LayoutService } from '@/layout/service/layout.service';
|
||||||
import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus';
|
import { getLicenseStatus } from '@/utils';
|
||||||
import { Component, computed, inject, signal, TemplateRef, ViewChild } from '@angular/core';
|
import { Component, computed, inject, signal, TemplateRef, ViewChild } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { Button, ButtonIcon } from 'primeng/button';
|
import { Button, ButtonIcon } from 'primeng/button';
|
||||||
@@ -20,15 +20,9 @@ export class LayoutComponent {
|
|||||||
|
|
||||||
visibleLicenseInfo = signal(false);
|
visibleLicenseInfo = signal(false);
|
||||||
|
|
||||||
licenseStatus = computed(() => {
|
licenseStatus = computed(() =>
|
||||||
if (!this.store.entity()?.license?.expires_at) {
|
getLicenseStatus(getLicenseStatus(this.store.entity()?.license_info?.expires_at)),
|
||||||
return null;
|
);
|
||||||
}
|
|
||||||
if (new Date().getTime() > new Date(this.store.entity()?.license?.expires_at || '').getTime()) {
|
|
||||||
return licenseStatus.EXPIRED;
|
|
||||||
}
|
|
||||||
return licenseStatus.ACTIVE;
|
|
||||||
});
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.layoutService.setMenuItems(CONSUMER_MENU_ITEMS);
|
this.layoutService.setMenuItems(CONSUMER_MENU_ITEMS);
|
||||||
|
|||||||
+12
-10
@@ -1,17 +1,19 @@
|
|||||||
import ISummary from '@/core/models/summary';
|
|
||||||
import { TLicenseStatus } from '@/shared/localEnum/constants/licenseStatus';
|
|
||||||
|
|
||||||
export interface IConsumerInfoRawResponse {
|
export interface IConsumerInfoRawResponse {
|
||||||
id: string;
|
id: string;
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
full_name: string;
|
|
||||||
mobile_number: string;
|
mobile_number: string;
|
||||||
license: {
|
status: string;
|
||||||
starts_at: string;
|
fullname: string;
|
||||||
expires_at: string;
|
license_info: LicenseInfo;
|
||||||
status: TLicenseStatus;
|
|
||||||
partner?: ISummary;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
export interface IConsumerInfoResponse extends IConsumerInfoRawResponse {}
|
export interface IConsumerInfoResponse extends IConsumerInfoRawResponse {}
|
||||||
|
|
||||||
|
interface LicenseInfo {
|
||||||
|
id: string;
|
||||||
|
starts_at: string;
|
||||||
|
expires_at: string;
|
||||||
|
partner: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
import ISummary from '@/core/models/summary';
|
import ISummary from '@/core/models/summary';
|
||||||
|
import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
|
||||||
|
import { CustomerIndividual, CustomerLegal } from './io';
|
||||||
|
|
||||||
export interface ICustomerSaleInvoicesRawResponse {
|
export interface ICustomerSaleInvoicesRawResponse {
|
||||||
id: string;
|
id: string;
|
||||||
code: string;
|
code: string;
|
||||||
invoice_date: string;
|
|
||||||
notes?: string;
|
|
||||||
total_amount: string;
|
total_amount: string;
|
||||||
|
invoice_date: string;
|
||||||
|
consumer_account: ConsumerAccount;
|
||||||
pos: Pos;
|
pos: Pos;
|
||||||
account: ConsumerAccount;
|
items: SaleInvoiceItem[];
|
||||||
|
payments: Payment[];
|
||||||
|
unknown_customer?: UnknownCustomer;
|
||||||
|
customer?: Customer;
|
||||||
|
notes?: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
items_count: number;
|
|
||||||
payments: Payments[];
|
|
||||||
}
|
}
|
||||||
export interface ICustomerSaleInvoicesResponse extends ICustomerSaleInvoicesRawResponse {}
|
export interface ICustomerSaleInvoicesResponse extends ICustomerSaleInvoicesRawResponse {}
|
||||||
|
|
||||||
interface ConsumerAccount {
|
interface ConsumerAccount {
|
||||||
role: string;
|
role: string;
|
||||||
user: User;
|
|
||||||
account: Account;
|
account: Account;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,11 +27,6 @@ interface Account {
|
|||||||
username: string;
|
username: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface User {
|
|
||||||
first_name: string;
|
|
||||||
last_name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Pos extends ISummary {
|
interface Pos extends ISummary {
|
||||||
complex: Complex;
|
complex: Complex;
|
||||||
}
|
}
|
||||||
@@ -37,8 +35,36 @@ interface Complex extends ISummary {
|
|||||||
business_activity: ISummary;
|
business_activity: ISummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Payments {
|
interface Payment {
|
||||||
amount: string;
|
amount: string;
|
||||||
paid_at: string;
|
paid_at: string;
|
||||||
payment_method: string;
|
payment_method: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SaleInvoiceItem {
|
||||||
|
id: string;
|
||||||
|
good: ISummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Customer {
|
||||||
|
id: string;
|
||||||
|
type: CustomerType;
|
||||||
|
customer_legal?: CustomerLegal;
|
||||||
|
customer_individual?: CustomerIndividual;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface CustomerIndividual {
|
||||||
|
// id: string;
|
||||||
|
// first_name: string;
|
||||||
|
// last_name: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface CustomerLegal {
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
interface UnknownCustomer {
|
||||||
|
name: string;
|
||||||
|
national_id: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<app-key-value label="کسب و کار" [value]="invoice()?.pos!.complex.business_activity.name" />
|
<app-key-value label="کسب و کار" [value]="invoice()?.pos!.complex.business_activity.name" />
|
||||||
<app-key-value label="مجموعه" [value]="invoice()?.pos!.complex.name" />
|
<app-key-value label="مجموعه" [value]="invoice()?.pos!.complex.name" />
|
||||||
<app-key-value label="پایانهی فروش" [value]="invoice()?.pos!.name" />
|
<app-key-value label="پایانهی فروش" [value]="invoice()?.pos!.name" />
|
||||||
<app-key-value label="ایجاد کننده" [value]="invoice()?.account?.account?.username" />
|
<app-key-value label="ایجاد کننده" [value]="invoice()?.consumer_account?.account?.username" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</app-card-data>
|
</app-card-data>
|
||||||
|
|||||||
+4
@@ -10,4 +10,8 @@
|
|||||||
<ng-template #moreActions>
|
<ng-template #moreActions>
|
||||||
<a routerLink pButton [routerLink]="invoicesPageRoute" outlined>تمامی فاکتورها</a>
|
<a routerLink pButton [routerLink]="invoicesPageRoute" outlined>تمامی فاکتورها</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #status let-item>
|
||||||
|
<p-badge value="ارسال نشده" severity="danger" />
|
||||||
|
</ng-template>
|
||||||
</app-page-data-list>
|
</app-page-data-list>
|
||||||
|
|||||||
+6
-7
@@ -1,8 +1,9 @@
|
|||||||
import { consumerSaleInvoicesNamedRoutes } from '@/domains/consumer/modules/saleInvoices/constants';
|
import { consumerSaleInvoicesNamedRoutes } from '@/domains/consumer/modules/saleInvoices/constants';
|
||||||
import { AbstractList } from '@/shared/abstractClasses';
|
import { AbstractList } from '@/shared/abstractClasses';
|
||||||
import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component';
|
import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component';
|
||||||
import { Component, inject } from '@angular/core';
|
import { Component, inject, TemplateRef, ViewChild } from '@angular/core';
|
||||||
import { Router, RouterLink } from '@angular/router';
|
import { Router, RouterLink } from '@angular/router';
|
||||||
|
import { Badge } from 'primeng/badge';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import { IStatisticsSaleInvoicesResponse } from '../../../models';
|
import { IStatisticsSaleInvoicesResponse } from '../../../models';
|
||||||
import { CustomerStatisticsService } from '../../../services/main.service';
|
import { CustomerStatisticsService } from '../../../services/main.service';
|
||||||
@@ -10,9 +11,11 @@ import { CustomerStatisticsService } from '../../../services/main.service';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'consumer-statistics-latest-Invoices',
|
selector: 'consumer-statistics-latest-Invoices',
|
||||||
templateUrl: './latest-Invoices.component.html',
|
templateUrl: './latest-Invoices.component.html',
|
||||||
imports: [PageDataListComponent, ButtonDirective, RouterLink],
|
imports: [PageDataListComponent, ButtonDirective, RouterLink, Badge],
|
||||||
})
|
})
|
||||||
export class ConsumerStatisticsLatestInvoicesComponent extends AbstractList<IStatisticsSaleInvoicesResponse> {
|
export class ConsumerStatisticsLatestInvoicesComponent extends AbstractList<IStatisticsSaleInvoicesResponse> {
|
||||||
|
@ViewChild('status', { static: true }) status!: TemplateRef<any>;
|
||||||
|
|
||||||
private readonly service = inject(CustomerStatisticsService);
|
private readonly service = inject(CustomerStatisticsService);
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
|
|
||||||
@@ -22,11 +25,7 @@ export class ConsumerStatisticsLatestInvoicesComponent extends AbstractList<ISta
|
|||||||
this.columns = [
|
this.columns = [
|
||||||
{ field: 'code', header: 'کد پیگیری' },
|
{ field: 'code', header: 'کد پیگیری' },
|
||||||
{ field: 'total_amount', header: 'مجموع قیمت', type: 'price' },
|
{ field: 'total_amount', header: 'مجموع قیمت', type: 'price' },
|
||||||
{
|
{ field: 'status', header: 'وضعیت صدور', customDataModel: this.status },
|
||||||
field: 'created_at',
|
|
||||||
header: 'تاریخ ایجاد',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export interface IGoodRawResponse {
|
|||||||
unit_type: UnitType;
|
unit_type: UnitType;
|
||||||
pricing_model: string;
|
pricing_model: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
image_url: string;
|
||||||
is_default_guild_good: boolean;
|
is_default_guild_good: boolean;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
} @else {
|
} @else {
|
||||||
@for (good of goods(); track good.id) {
|
@for (good of goods(); track good.id) {
|
||||||
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs">
|
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs">
|
||||||
<img [src]="goodPlaceholder" class="w-full aspect-[1.3] object-cover rounded-md" />
|
<img [src]="good.image_url || goodPlaceholder" class="w-full aspect-[1.3] object-cover rounded-md" />
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<span class="text-lg font-bold">
|
<span class="text-lg font-bold">
|
||||||
{{ good.name }}
|
{{ good.name }}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
@for (good of goods(); track good.id) {
|
@for (good of goods(); track good.id) {
|
||||||
<div class="flex items-center bg-surface-card rounded-lg p-1 shadow-xs gap-2">
|
<div class="flex items-center bg-surface-card rounded-lg p-1 shadow-xs gap-2">
|
||||||
<div class="grow flex items-center gap-2">
|
<div class="grow flex items-center gap-2">
|
||||||
<img [src]="goodPlaceholder" class="w-12 h-12 object-cover rounded-md" />
|
<img [src]="good.image_url || goodPlaceholder" class="w-12 h-12 object-cover rounded-md" />
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<span class="text-lg font-bold">
|
<span class="text-lg font-bold">
|
||||||
{{ good.name }}
|
{{ good.name }}
|
||||||
|
|||||||
+29
-17
@@ -1,11 +1,33 @@
|
|||||||
<div class="border border-surface-border rounded-xl p-2 shadow-sm">
|
<div class="border border-surface-border rounded-xl p-2 shadow-sm">
|
||||||
<div class="flex gap-3 items-stretch">
|
<div class="flex gap-3 items-center">
|
||||||
<img [src]="placeholderImage" class="w-24 h-24 rounded-md bg-surface-100 object-cover shrink-0" />
|
<img
|
||||||
<div class="flex flex-col grow justify-between">
|
[src]="inOrderGood.good.image_url || placeholderImage"
|
||||||
<div class="flex items-center">
|
class="w-24 h-24 rounded-md bg-surface-100 object-cover shrink-0 border border-surface-border"
|
||||||
<div class="flex flex-col grow">
|
/>
|
||||||
<span class="font-bold text-lg">{{ inOrderGood.good.name }}</span>
|
<div class="flex flex-col grow justify-between overflow-hidden">
|
||||||
<span class="text-sm text-muted-color">{{ inOrderGood.good.sku }}</span>
|
<div class="flex items-stretch grow gap-4">
|
||||||
|
<div class="flex flex-col justify-between gap-2 grow overflow-hidden py-1">
|
||||||
|
<div class="shrink-0 flex flex-col w-full overflow-hidden">
|
||||||
|
<div class="flex w-full overflow-hidden gap-1 items-center">
|
||||||
|
<span class="font-bold text-lg text-ellipsis text-nowrap overflow-hidden">
|
||||||
|
{{ inOrderGood.good.name }}
|
||||||
|
</span>
|
||||||
|
<div class="grow">
|
||||||
|
<span class="text-sm text-muted-color">({{ inOrderGood.good.sku }})</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="text-sm text-ellipsis text-nowrap overflow-hidden">
|
||||||
|
{{ inOrderGood.quantity }} {{ enumTranslator(inOrderGood.good.unit_type) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@if (!inOrderGood.discount_amount) {
|
||||||
|
<span [appPriceMask]="inOrderGood.total_amount"></span>
|
||||||
|
} @else {
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="line-through text-muted-color text-xs" [appPriceMask]="inOrderGood.base_total_amount"></span>
|
||||||
|
<span [appPriceMask]="inOrderGood.base_total_amount - inOrderGood.discount_amount"></span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex shrink-0 gap-1">
|
<div class="flex shrink-0 gap-1">
|
||||||
<button
|
<button
|
||||||
@@ -28,16 +50,6 @@
|
|||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4 mt-auto">
|
|
||||||
<div class="ms-auto shrink-0">
|
|
||||||
<!-- <app-uikit-counter
|
|
||||||
[min]="1"
|
|
||||||
[max]="inOrderGood.maxQuantity"
|
|
||||||
[value]="inOrderGood.count"
|
|
||||||
(valueChange)="updateInOrderGoodQuantity(inOrderGood.goodId, $event)"
|
|
||||||
></app-uikit-counter> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+4
-1
@@ -1,3 +1,5 @@
|
|||||||
|
import { PriceMaskDirective } from '@/shared/directives';
|
||||||
|
import { enumTranslator } from '@/utils';
|
||||||
import { Component, inject, Input, signal } from '@angular/core';
|
import { Component, inject, Input, signal } from '@angular/core';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import images from 'src/assets/images';
|
import images from 'src/assets/images';
|
||||||
@@ -8,7 +10,7 @@ import { PayloadFormDialogComponent } from '../payload-form.component';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-order-card-template',
|
selector: 'pos-order-card-template',
|
||||||
templateUrl: './order-card-template.component.html',
|
templateUrl: './order-card-template.component.html',
|
||||||
imports: [ButtonDirective, PayloadFormDialogComponent],
|
imports: [ButtonDirective, PayloadFormDialogComponent, PriceMaskDirective],
|
||||||
})
|
})
|
||||||
export class OrderCardTemplateComponent {
|
export class OrderCardTemplateComponent {
|
||||||
@Input({ required: true }) inOrderGood!: IPosInOrderGood;
|
@Input({ required: true }) inOrderGood!: IPosInOrderGood;
|
||||||
@@ -18,6 +20,7 @@ export class OrderCardTemplateComponent {
|
|||||||
visible = signal(false);
|
visible = signal(false);
|
||||||
|
|
||||||
placeholderImage = images.placeholders.default;
|
placeholderImage = images.placeholders.default;
|
||||||
|
enumTranslator = (key: string) => enumTranslator(key);
|
||||||
|
|
||||||
removeGoodFromOrder() {
|
removeGoodFromOrder() {
|
||||||
this.store.removeFromInOrderGoods(this.inOrderGood.id);
|
this.store.removeFromInOrderGoods(this.inOrderGood.id);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
[percentageControl]="form.controls.payload.controls.wages_percentage"
|
[percentageControl]="form.controls.payload.controls.wages_percentage"
|
||||||
[amountControl]="form.controls.payload.controls.wages_amount"
|
[amountControl]="form.controls.payload.controls.wages_amount"
|
||||||
[baseAmount]="unitWithQuantity()"
|
[baseAmount]="unitWithQuantity()"
|
||||||
|
[minAmount]="0"
|
||||||
|
[maxAmount]="unitWithQuantity()"
|
||||||
name="wages"
|
name="wages"
|
||||||
label="اجرت"
|
label="اجرت"
|
||||||
/>
|
/>
|
||||||
@@ -14,6 +16,8 @@
|
|||||||
[percentageControl]="form.controls.payload.controls.commission_percentage"
|
[percentageControl]="form.controls.payload.controls.commission_percentage"
|
||||||
[amountControl]="form.controls.payload.controls.commission_amount"
|
[amountControl]="form.controls.payload.controls.commission_amount"
|
||||||
[baseAmount]="unitWithQuantity()"
|
[baseAmount]="unitWithQuantity()"
|
||||||
|
[minAmount]="0"
|
||||||
|
[maxAmount]="unitWithQuantity()"
|
||||||
name="commission"
|
name="commission"
|
||||||
label="حقالعمل"
|
label="حقالعمل"
|
||||||
/>
|
/>
|
||||||
@@ -21,6 +25,8 @@
|
|||||||
[percentageControl]="form.controls.payload.controls.profit_percentage"
|
[percentageControl]="form.controls.payload.controls.profit_percentage"
|
||||||
[amountControl]="form.controls.payload.controls.profit_amount"
|
[amountControl]="form.controls.payload.controls.profit_amount"
|
||||||
[baseAmount]="totalAmountBeforeProfit()"
|
[baseAmount]="totalAmountBeforeProfit()"
|
||||||
|
[minAmount]="0"
|
||||||
|
[maxAmount]="totalAmountBeforeProfit()"
|
||||||
name="profit"
|
name="profit"
|
||||||
label="سود"
|
label="سود"
|
||||||
/>
|
/>
|
||||||
@@ -29,6 +35,8 @@
|
|||||||
[percentageControl]="form.controls.discount_percentage"
|
[percentageControl]="form.controls.discount_percentage"
|
||||||
[amountControl]="form.controls.discount_amount"
|
[amountControl]="form.controls.discount_amount"
|
||||||
[baseAmount]="baseTotalAmount()"
|
[baseAmount]="baseTotalAmount()"
|
||||||
|
[minAmount]="0"
|
||||||
|
[maxAmount]="baseTotalAmount()"
|
||||||
name="discount"
|
name="discount"
|
||||||
label="تخفیف"
|
label="تخفیف"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { InputComponent } from '@/shared/components';
|
|||||||
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
|
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
|
||||||
import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat';
|
import { TGoldKarat } from '@/shared/localEnum/constants/goldKarat';
|
||||||
import { formatWithCurrency } from '@/utils';
|
import { formatWithCurrency } from '@/utils';
|
||||||
import { Component, computed, EventEmitter, Output } from '@angular/core';
|
import { Component, EventEmitter, Output, signal } from '@angular/core';
|
||||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { Button } from 'primeng/button';
|
import { Button } from 'primeng/button';
|
||||||
import { IGoldPayload, IPosOrderItem } from '../../../models';
|
import { IGoldPayload, IPosOrderItem } from '../../../models';
|
||||||
@@ -29,30 +29,54 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
|||||||
@Output() onSubmitForm = new EventEmitter<IGoldPayload>();
|
@Output() onSubmitForm = new EventEmitter<IGoldPayload>();
|
||||||
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
@Output() onChangeTotalAmount = new EventEmitter<number>();
|
||||||
|
|
||||||
|
unitWithQuantity = signal<number>(0);
|
||||||
|
totalAmountBeforeProfit = signal<number>(0);
|
||||||
|
baseTotalAmount = signal<number>(0);
|
||||||
|
taxAmount = signal<number>(0);
|
||||||
|
totalAmount = signal<number>(0);
|
||||||
|
|
||||||
private readonly initialForm = () => {
|
private readonly initialForm = () => {
|
||||||
const form = this.fb.group({
|
const form = this.fb.group({
|
||||||
unit_price: [this.initialValues?.unit_price || 200_000_000, [Validators.required]],
|
unit_price: [this.initialValues?.unit_price || 0, [Validators.required]],
|
||||||
quantity: [this.initialValues?.quantity || 2, [Validators.required]],
|
quantity: [this.initialValues?.quantity || 0, [Validators.required]],
|
||||||
discount_amount: [this.initialValues?.discount || 0],
|
discount_amount: [this.initialValues?.discount || 0],
|
||||||
discount_percentage: [this.initialValues?.discount || 0],
|
discount_percentage: [0, [Validators.max(100), Validators.min(0)]],
|
||||||
payload: this.fb.group({
|
payload: this.fb.group({
|
||||||
commission_amount: [this.initialValues?.payload?.commission || 0, [Validators.required]],
|
commission_amount: [this.initialValues?.payload?.commission || 0, [Validators.required]],
|
||||||
commission_percentage: [
|
commission_percentage: [0, [Validators.required, Validators.max(100), Validators.min(0)]],
|
||||||
this.initialValues?.payload?.commission || 10,
|
|
||||||
[Validators.required],
|
|
||||||
],
|
|
||||||
wages_amount: [this.initialValues?.payload?.wages || 0, [Validators.required]],
|
wages_amount: [this.initialValues?.payload?.wages || 0, [Validators.required]],
|
||||||
wages_percentage: [this.initialValues?.payload?.wages || 10, [Validators.required]],
|
wages_percentage: [0, [Validators.required, Validators.max(100), Validators.min(0)]],
|
||||||
profit_amount: [this.initialValues?.payload?.profit || 0, [Validators.required]],
|
profit_amount: [this.initialValues?.payload?.profit || 0, [Validators.required]],
|
||||||
profit_percentage: [this.initialValues?.payload?.profit || 10, [Validators.required]],
|
profit_percentage: [0, [Validators.required, Validators.max(100), Validators.min(0)]],
|
||||||
karat: [this.initialValues?.payload?.karat || '', [Validators.required]],
|
karat: [this.initialValues?.payload?.karat || '', [Validators.required]],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
form.valueChanges.subscribe((value) => {
|
||||||
|
this.updateCalculateAmount(value as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
form.setValue({
|
||||||
|
unit_price: 200_000_000,
|
||||||
|
quantity: 2,
|
||||||
|
discount_amount: 0,
|
||||||
|
discount_percentage: 0,
|
||||||
|
payload: {
|
||||||
|
commission_amount: 0,
|
||||||
|
commission_percentage: 10,
|
||||||
|
wages_amount: 0,
|
||||||
|
wages_percentage: 10,
|
||||||
|
profit_amount: 0,
|
||||||
|
profit_percentage: 10,
|
||||||
|
karat: '18',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return form;
|
return form;
|
||||||
};
|
};
|
||||||
|
|
||||||
form = this.initialForm();
|
form = this.initialForm();
|
||||||
|
|
||||||
override submitForm(payload: IPosOrderItem) {
|
override submitForm(payload: IPosOrderItem) {
|
||||||
this.onSubmit.emit({
|
this.onSubmit.emit({
|
||||||
...payload,
|
...payload,
|
||||||
@@ -68,36 +92,28 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unitWithQuantity = computed(
|
updateCalculateAmount(payload: Partial<IPosOrderItem<any>>) {
|
||||||
() => (this.form.value.unit_price ?? 0) * (this.form.value.quantity ?? 0),
|
const commissionAmount = Number(payload.payload?.commission_amount ?? '0');
|
||||||
);
|
const wageAmount = Number(payload.payload?.wages ?? '0');
|
||||||
|
const discountAmount = Number(payload.discount_amount ?? '0');
|
||||||
|
const profitAmount = Number(payload.payload?.profit_amount ?? '0');
|
||||||
|
const unitPrice = Number(payload.unit_price ?? '0');
|
||||||
|
const quantity = Number(payload.quantity ?? '0');
|
||||||
|
|
||||||
totalAmountBeforeProfit = computed(() => {
|
const unitWithQuantity = unitPrice * quantity;
|
||||||
const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0');
|
const totalAmountBeforeProfit = unitWithQuantity + wageAmount + commissionAmount;
|
||||||
const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0');
|
const baseTotalAmount = totalAmountBeforeProfit + profitAmount;
|
||||||
return this.unitWithQuantity() + wageAmount + commissionAmount;
|
const taxAmount = (profitAmount + commissionAmount + wageAmount - discountAmount) * 0.1;
|
||||||
});
|
const totalAmount = baseTotalAmount - discountAmount + taxAmount;
|
||||||
|
|
||||||
baseTotalAmount = computed(() => {
|
this.unitWithQuantity.set(unitWithQuantity);
|
||||||
const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0');
|
this.totalAmountBeforeProfit.set(totalAmountBeforeProfit);
|
||||||
const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0');
|
this.baseTotalAmount.set(baseTotalAmount);
|
||||||
const profitAmount = Number(this.form.value.payload?.profit_amount ?? '0');
|
this.taxAmount.set(taxAmount);
|
||||||
|
this.totalAmount.set(totalAmount);
|
||||||
|
|
||||||
return this.unitWithQuantity() + profitAmount + commissionAmount + wageAmount;
|
this.onChangeTotalAmount.emit(totalAmount);
|
||||||
});
|
}
|
||||||
|
|
||||||
taxAmount = computed(() => {
|
|
||||||
const commissionAmount = Number(this.form.value.payload?.commission_amount ?? '0');
|
|
||||||
const wageAmount = Number(this.form.value.payload?.wages_amount ?? '0');
|
|
||||||
const profitAmount = Number(this.form.value.payload?.profit_amount ?? '0');
|
|
||||||
const discountAmount = Number(this.form.value.discount_amount ?? '0');
|
|
||||||
return (profitAmount + commissionAmount + wageAmount - discountAmount) * 0.1;
|
|
||||||
});
|
|
||||||
|
|
||||||
totalAmount = computed(() => {
|
|
||||||
const discountAmount = Number(this.form.value.discount_amount ?? '0');
|
|
||||||
return this.baseTotalAmount() - discountAmount + this.taxAmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
toAmountFormat(amount: number) {
|
toAmountFormat(amount: number) {
|
||||||
if (!amount) {
|
if (!amount) {
|
||||||
|
|||||||
+7
-6
@@ -9,15 +9,16 @@
|
|||||||
[min]="0"
|
[min]="0"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<app-input
|
<app-amount-percentage-input
|
||||||
[control]="form.controls.discount"
|
[percentageControl]="form.controls.discount_percentage"
|
||||||
|
[amountControl]="form.controls.discount_amount"
|
||||||
|
[baseAmount]="baseTotalAmount()"
|
||||||
|
[minAmount]="0"
|
||||||
|
[maxAmount]="baseTotalAmount()"
|
||||||
name="discount"
|
name="discount"
|
||||||
label="تخفیف"
|
label="تخفیف"
|
||||||
type="number"
|
|
||||||
suffix="درصد"
|
|
||||||
[min]="0"
|
|
||||||
[max]="100"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
<div class="flex flex-col gap-4 justify-center w-full">
|
<div class="flex flex-col gap-4 justify-center w-full">
|
||||||
<pos-form-dialog-amount-card-template
|
<pos-form-dialog-amount-card-template
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { AbstractForm } from '@/shared/abstractClasses';
|
import { AbstractForm } from '@/shared/abstractClasses';
|
||||||
import { InputComponent } from '@/shared/components';
|
import { InputComponent } from '@/shared/components';
|
||||||
|
import { AmountPercentageInputComponent } from '@/shared/components/amountPercentageInput/amount-percentage-input.component';
|
||||||
import { formatWithCurrency, getGoodUnitTypeProperties, UnitType } from '@/utils';
|
import { formatWithCurrency, getGoodUnitTypeProperties, UnitType } from '@/utils';
|
||||||
import { Component, computed, EventEmitter, Input, Output, signal } from '@angular/core';
|
import { Component, computed, EventEmitter, Input, Output, signal } from '@angular/core';
|
||||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
@@ -10,7 +11,13 @@ import { PosFormDialogAmountCardTemplateComponent } from '../form-dialog-amount-
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-standard-payload-form',
|
selector: 'pos-standard-payload-form',
|
||||||
templateUrl: './form.component.html',
|
templateUrl: './form.component.html',
|
||||||
imports: [ReactiveFormsModule, InputComponent, Button, PosFormDialogAmountCardTemplateComponent],
|
imports: [
|
||||||
|
ReactiveFormsModule,
|
||||||
|
InputComponent,
|
||||||
|
Button,
|
||||||
|
PosFormDialogAmountCardTemplateComponent,
|
||||||
|
AmountPercentageInputComponent,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class PosStandardPayloadFormComponent extends AbstractForm<
|
export class PosStandardPayloadFormComponent extends AbstractForm<
|
||||||
IPosOrderItem<IStandardPayload>,
|
IPosOrderItem<IStandardPayload>,
|
||||||
@@ -23,7 +30,8 @@ export class PosStandardPayloadFormComponent extends AbstractForm<
|
|||||||
const form = this.fb.group({
|
const form = this.fb.group({
|
||||||
unit_price: [this.initialValues?.unit_price || 0, [Validators.required]],
|
unit_price: [this.initialValues?.unit_price || 0, [Validators.required]],
|
||||||
quantity: [this.initialValues?.quantity || 0, [Validators.required]],
|
quantity: [this.initialValues?.quantity || 0, [Validators.required]],
|
||||||
discount: [this.initialValues?.discount || ''],
|
discount_amount: [this.initialValues?.discount || 0, [Validators.min(0)]],
|
||||||
|
discount_percentage: [0, [Validators.min(0), Validators.max(100)]],
|
||||||
});
|
});
|
||||||
|
|
||||||
form.valueChanges.subscribe((value) => {
|
form.valueChanges.subscribe((value) => {
|
||||||
@@ -60,7 +68,7 @@ export class PosStandardPayloadFormComponent extends AbstractForm<
|
|||||||
|
|
||||||
updateCalculateAmount(value: Partial<IPosOrderItem<IStandardPayload>>) {
|
updateCalculateAmount(value: Partial<IPosOrderItem<IStandardPayload>>) {
|
||||||
const baseTotalAmount = (value.unit_price ?? 0) * (value.quantity ?? 0);
|
const baseTotalAmount = (value.unit_price ?? 0) * (value.quantity ?? 0);
|
||||||
const discountAmount = (baseTotalAmount * Number(value.discount || '0')) / 100;
|
const discountAmount = Number(value.discount_amount ?? '0');
|
||||||
const baseTotalAmountWithoutTax = baseTotalAmount - discountAmount;
|
const baseTotalAmountWithoutTax = baseTotalAmount - discountAmount;
|
||||||
const taxAmount = baseTotalAmountWithoutTax * 0.1;
|
const taxAmount = baseTotalAmountWithoutTax * 0.1;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface IPosOrderItem<T = TPosOrderGoodPayload> {
|
|||||||
good_id?: string;
|
good_id?: string;
|
||||||
service_id?: string;
|
service_id?: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
|
image_url?: string;
|
||||||
payload: T;
|
payload: T;
|
||||||
|
|
||||||
base_total_amount: number;
|
base_total_amount: number;
|
||||||
|
|||||||
@@ -233,19 +233,22 @@ export class PosLandingStore {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
map((_res) => {
|
map((_res) => {
|
||||||
// this.setState({
|
if (_res) {
|
||||||
// inOrderGoods: [],
|
this.setState({
|
||||||
// customerDetails: {
|
inOrderGoods: [],
|
||||||
// type: CustomerType.UNKNOWN,
|
customerDetails: {
|
||||||
// },
|
type: CustomerType.UNKNOWN,
|
||||||
// invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC),
|
},
|
||||||
// payments: {
|
invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC),
|
||||||
// cash: 0,
|
payments: {
|
||||||
// set_off: 0,
|
cash: 0,
|
||||||
// terminal: 0,
|
set_off: 0,
|
||||||
// },
|
terminal: 0,
|
||||||
// orderNote: '',
|
},
|
||||||
// });
|
orderNote: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return _res;
|
return _res;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BreadcrumbService } from '@/core/services';
|
import { BreadcrumbService } from '@/core/services';
|
||||||
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
|
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
|
||||||
import { JalaliDateDirective } from '@/shared/directives';
|
import { JalaliDateDirective } from '@/shared/directives';
|
||||||
import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus';
|
import { getLicenseStatus } from '@/utils';
|
||||||
import { Component, computed, effect, inject, signal } from '@angular/core';
|
import { Component, computed, effect, inject, signal } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Button } from 'primeng/button';
|
import { Button } from 'primeng/button';
|
||||||
@@ -38,19 +38,9 @@ export class ConsumerComponent {
|
|||||||
readonly loading = computed(() => this.store.loading());
|
readonly loading = computed(() => this.store.loading());
|
||||||
readonly consumer = computed(() => this.store.entity());
|
readonly consumer = computed(() => this.store.entity());
|
||||||
readonly license = computed(() => this.store.entity()?.license_info);
|
readonly license = computed(() => this.store.entity()?.license_info);
|
||||||
readonly licenseStatus = computed(() => {
|
readonly licenseStatus = computed(() =>
|
||||||
console.log(this.store.entity()?.license_info?.expires_at);
|
getLicenseStatus(this.store.entity()?.license_info?.expires_at),
|
||||||
|
);
|
||||||
if (!this.store.entity()?.license_info?.expires_at) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
new Date().getTime() > new Date(this.store.entity()?.license_info?.expires_at || '').getTime()
|
|
||||||
) {
|
|
||||||
return licenseStatus.EXPIRED;
|
|
||||||
}
|
|
||||||
return licenseStatus.ACTIVE;
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<p-dialog [header]="preparedTitle" [(visible)]="visible" [modal]="true" [style]="{ width: '500px' }" [closable]="true">
|
<p-dialog [header]="preparedTitle" [(visible)]="visible" [modal]="true" [style]="{ width: '500px' }" [closable]="true">
|
||||||
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
||||||
|
<app-shared-upload-file accept="image/*" name="image" (onSelect)="changeFile($event)" />
|
||||||
<app-input label="عنوان" [control]="form.controls.name" name="name" />
|
<app-input label="عنوان" [control]="form.controls.name" name="name" />
|
||||||
<app-input label="شناسه عمومی" [control]="form.controls.sku" name="sku" />
|
<app-input label="شناسه عمومی" [control]="form.controls.sku" name="sku" />
|
||||||
<admin-guild-categories-select
|
<admin-guild-categories-select
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||||
import { InputComponent } from '@/shared/components';
|
import { InputComponent } from '@/shared/components';
|
||||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||||
import { Component, inject, Input } from '@angular/core';
|
import { Component, inject, Input, signal } from '@angular/core';
|
||||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { Dialog } from 'primeng/dialog';
|
import { Dialog } from 'primeng/dialog';
|
||||||
|
|
||||||
|
import { Maybe } from '@/core';
|
||||||
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
import { EnumSelectComponent } from '@/shared/apiEnum/select.component';
|
||||||
|
import { onSelectFileArgs } from '@/shared/components/uploadFile/model';
|
||||||
|
import { SharedUploadFileComponent } from '@/shared/components/uploadFile/upload-file.component';
|
||||||
|
import { buildFormData } from '@/utils';
|
||||||
import { IGuildGoodRequest, IGuildGoodsResponse } from '../../models';
|
import { IGuildGoodRequest, IGuildGoodsResponse } from '../../models';
|
||||||
import { GuildGoodsService } from '../../services/goods.service';
|
import { GuildGoodsService } from '../../services/goods.service';
|
||||||
import { GuildCategoriesSelectComponent } from '../categories/select.component';
|
import { GuildCategoriesSelectComponent } from '../categories/select.component';
|
||||||
@@ -20,6 +24,7 @@ import { GuildCategoriesSelectComponent } from '../categories/select.component';
|
|||||||
FormFooterActionsComponent,
|
FormFooterActionsComponent,
|
||||||
EnumSelectComponent,
|
EnumSelectComponent,
|
||||||
GuildCategoriesSelectComponent,
|
GuildCategoriesSelectComponent,
|
||||||
|
SharedUploadFileComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GuildGoodFormComponent extends AbstractFormDialog<
|
export class GuildGoodFormComponent extends AbstractFormDialog<
|
||||||
@@ -31,6 +36,8 @@ export class GuildGoodFormComponent extends AbstractFormDialog<
|
|||||||
|
|
||||||
private service = inject(GuildGoodsService);
|
private service = inject(GuildGoodsService);
|
||||||
|
|
||||||
|
goodImage = signal<Maybe<File>>(null);
|
||||||
|
|
||||||
form = this.fb.group({
|
form = this.fb.group({
|
||||||
name: [this.initialValues?.name || '', [Validators.required]],
|
name: [this.initialValues?.name || '', [Validators.required]],
|
||||||
sku: [this.initialValues?.sku || '', [Validators.required]],
|
sku: [this.initialValues?.sku || '', [Validators.required]],
|
||||||
@@ -44,10 +51,20 @@ export class GuildGoodFormComponent extends AbstractFormDialog<
|
|||||||
return `${this.editMode ? 'ویرایش' : 'ایجاد'} کالا`;
|
return `${this.editMode ? 'ویرایش' : 'ایجاد'} کالا`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeFile(payload: onSelectFileArgs) {
|
||||||
|
this.goodImage.set(payload.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
override ngOnChanges(): void {
|
||||||
|
this.form.patchValue(this.initialValues || {});
|
||||||
|
this.form.controls.category_id.setValue(this.initialValues?.category.id || '');
|
||||||
|
}
|
||||||
|
|
||||||
override submitForm(payload: IGuildGoodRequest) {
|
override submitForm(payload: IGuildGoodRequest) {
|
||||||
|
const formData = buildFormData(this.form, { image: this.goodImage() });
|
||||||
if (this.editMode) {
|
if (this.editMode) {
|
||||||
return this.service.update(this.guildId, this.categoryId!, payload);
|
return this.service.update(this.guildId, this.categoryId!, formData);
|
||||||
}
|
}
|
||||||
return this.service.create(this.guildId, payload);
|
return this.service.create(this.guildId, formData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { GUILD_GOODS_API_ROUTES } from '../constants/apiRoutes/goods';
|
import { GUILD_GOODS_API_ROUTES } from '../constants/apiRoutes/goods';
|
||||||
import { IGuildGoodRequest, IGuildGoodsRawResponse, IGuildGoodsResponse } from '../models';
|
import { IGuildGoodsRawResponse, IGuildGoodsResponse } from '../models';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class GuildGoodsService {
|
export class GuildGoodsService {
|
||||||
@@ -18,11 +18,11 @@ export class GuildGoodsService {
|
|||||||
return this.http.get<IGuildGoodsRawResponse>(this.apiRoutes.single(guildId, id));
|
return this.http.get<IGuildGoodsRawResponse>(this.apiRoutes.single(guildId, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
create(guildId: string, data: IGuildGoodRequest): Observable<IGuildGoodsResponse> {
|
create(guildId: string, data: FormData): Observable<IGuildGoodsResponse> {
|
||||||
return this.http.post<IGuildGoodsResponse>(this.apiRoutes.list(guildId), data);
|
return this.http.post<IGuildGoodsResponse>(this.apiRoutes.list(guildId), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(guildId: string, id: string, data: IGuildGoodRequest): Observable<IGuildGoodsResponse> {
|
update(guildId: string, id: string, data: FormData): Observable<IGuildGoodsResponse> {
|
||||||
return this.http.patch<IGuildGoodsResponse>(this.apiRoutes.single(guildId, id), data);
|
return this.http.patch<IGuildGoodsResponse>(this.apiRoutes.single(guildId, id), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ export abstract class AbstractForm<
|
|||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.form.patchValue(this.initialValues ?? {});
|
||||||
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.form.patchValue(this.initialValues ?? {});
|
this.form.patchValue(this.initialValues ?? {});
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-5
@@ -79,14 +79,27 @@ export class AmountPercentageInputComponent {
|
|||||||
onInput($event: Event, isPriceType: boolean = false) {
|
onInput($event: Event, isPriceType: boolean = false) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let value = $event.target.value as string;
|
let value = $event.target.value as string;
|
||||||
|
// @ts-ignore
|
||||||
|
const isDotInput = $event.data === '.';
|
||||||
|
|
||||||
|
if (isDotInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isPriceType) {
|
if (isPriceType) {
|
||||||
value = value.replace(/,/g, '');
|
value = value.replace(/,/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modifyControlsData(value);
|
const newValueToSet = this.modifyControlsData(value);
|
||||||
|
|
||||||
|
if (newValueToSet !== null) {
|
||||||
|
// @ts-ignore
|
||||||
|
$event.target.value = newValueToSet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private modifyControlsData(value: string) {
|
private modifyControlsData(value: string) {
|
||||||
|
let newValueToSet: Maybe<string> = null;
|
||||||
let newValue = parseFloat(value);
|
let newValue = parseFloat(value);
|
||||||
const isPercentageType = this.selectedType.value === 'percentage';
|
const isPercentageType = this.selectedType.value === 'percentage';
|
||||||
|
|
||||||
@@ -96,7 +109,9 @@ export class AmountPercentageInputComponent {
|
|||||||
const maxValidator = max < newValue;
|
const maxValidator = max < newValue;
|
||||||
const minValidator = min > newValue;
|
const minValidator = min > newValue;
|
||||||
|
|
||||||
if (minValidator || maxValidator) {
|
const notValid = minValidator || maxValidator;
|
||||||
|
|
||||||
|
if (notValid) {
|
||||||
if (minValidator) {
|
if (minValidator) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
text: `مقدار فیلد باید بیشتر از ${min} باشد.`,
|
text: `مقدار فیلد باید بیشتر از ${min} باشد.`,
|
||||||
@@ -116,16 +131,23 @@ export class AmountPercentageInputComponent {
|
|||||||
|
|
||||||
if (isPercentageType) {
|
if (isPercentageType) {
|
||||||
const amountValue = (this.baseAmount * newValue) / 100;
|
const amountValue = (this.baseAmount * newValue) / 100;
|
||||||
|
newValueToSet = newValue + '';
|
||||||
this.amountControl.setValue(amountValue);
|
this.amountControl.setValue(amountValue);
|
||||||
|
if (notValid) {
|
||||||
|
this.percentageControl.setValue(newValue);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const percentageValue = (newValue / this.baseAmount) * 100;
|
const percentageValue = (newValue / this.baseAmount) * 100;
|
||||||
|
newValueToSet = newValue + '';
|
||||||
this.percentageControl.setValue(percentageValue);
|
this.percentageControl.setValue(percentageValue);
|
||||||
this.preparedLabel.set(`${this.label} (${percentageValue} درصد)`);
|
this.preparedLabel.set(`${this.label} (${percentageValue} درصد)`);
|
||||||
// @ts-ignore
|
if (notValid) {
|
||||||
// $event.target.value = newValue;
|
this.amountControl.setValue(newValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.preparedLabel.set(this.prepareLabel(isPercentageType));
|
this.preparedLabel.set(this.prepareLabel(isPercentageType));
|
||||||
|
|
||||||
|
return newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private prepareLabel(isPercentageType: boolean) {
|
private prepareLabel(isPercentageType: boolean) {
|
||||||
@@ -145,4 +167,15 @@ export class AmountPercentageInputComponent {
|
|||||||
this.preparedLabel.set(this.prepareLabel(value === 'percentage'));
|
this.preparedLabel.set(this.prepareLabel(value === 'percentage'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
const isPercentageType = this.selectedType.value === 'percentage';
|
||||||
|
this.modifyControlsData(
|
||||||
|
isPercentageType ? this.percentageControl.value : this.amountControl.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.selectedType.valueChanges.subscribe((value) => {
|
||||||
|
this.preparedLabel.set(this.prepareLabel(value === 'percentage'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,26 +189,27 @@ export class InputComponent {
|
|||||||
onInput($event: Event, isPriceFormat?: boolean) {
|
onInput($event: Event, isPriceFormat?: boolean) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let value = $event.target.value as string;
|
let value = $event.target.value as string;
|
||||||
|
// @ts-ignore
|
||||||
|
const isDotInput = $event.data === '.';
|
||||||
|
|
||||||
if (isPriceFormat) {
|
if (isPriceFormat) {
|
||||||
value = value.replace(/,/g, '');
|
value = value.replace(/,/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.inputMode === 'numeric') {
|
let replacedTargetValue: Maybe<number | string> = null;
|
||||||
let newValue = parseFloat(value);
|
if (this.inputMode === 'numeric' && !isDotInput) {
|
||||||
|
let newValue = parseFloat(value || '0');
|
||||||
const minValidator = (this.min || this.min === 0) && parseFloat(value) < this.min!;
|
const minValidator = (this.min || this.min === 0) && parseFloat(value) < this.min!;
|
||||||
const maxValidator = (this.max || this.max === 0) && parseFloat(value) > this.max;
|
const maxValidator = (this.max || this.max === 0) && parseFloat(value) > this.max;
|
||||||
|
|
||||||
if (minValidator || maxValidator) {
|
if (minValidator || maxValidator) {
|
||||||
if (minValidator) {
|
if (minValidator) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
text: `مقدار فیلد باید بیشتر از ${this.min} باشد.`,
|
text: `مقدار ${this.label} نمیتواند بیشتر از ${this.min} باشد.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (maxValidator) {
|
if (maxValidator) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
text: `مقدار فیلد باید کمتر از ${this.max} باشد.`,
|
text: `مقدار ${this.label} نمیتواند کمتر از ${this.max} باشد.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,23 +218,23 @@ export class InputComponent {
|
|||||||
newValue = 0;
|
newValue = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPriceFormat) {
|
replacedTargetValue = isPriceFormat ? newValue.toLocaleString('en-US') : newValue;
|
||||||
// @ts-ignore
|
|
||||||
$event.target.value = newValue.toLocaleString('en-US');
|
|
||||||
} else {
|
|
||||||
// @ts-ignore
|
|
||||||
$event.target.value = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!['nationalId', 'phone', 'postalCode', 'mobile'].includes(this.type))
|
|
||||||
this.control.setValue(newValue);
|
this.control.setValue(newValue);
|
||||||
|
}
|
||||||
|
if (!['nationalId', 'phone', 'postalCode', 'mobile'].includes(this.type)) {
|
||||||
|
this.control.setValue(newValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.preparedMaxLength && this.preparedMaxLength < value.length) {
|
if (this.preparedMaxLength && this.preparedMaxLength < value.length) {
|
||||||
let newValue = value.slice(0, value.length - 1);
|
let newValue = value.slice(0, value.length - 1);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
$event.target.value = newValue;
|
replacedTargetValue = newValue;
|
||||||
this.control.setValue(newValue);
|
this.control.setValue(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (replacedTargetValue !== null) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.$event.value = replacedTargetValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
export interface onSelectArgs {
|
export interface onSelectFileArgs {
|
||||||
data: FormData;
|
data: FormData;
|
||||||
file: File;
|
file: File;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Maybe } from '@/core';
|
import { Maybe } from '@/core';
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { FileSelectEvent, FileUpload } from 'primeng/fileupload';
|
import { FileSelectEvent, FileUpload } from 'primeng/fileupload';
|
||||||
import { onSelectArgs } from './model';
|
import { onSelectFileArgs } from './model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-shared-upload-file',
|
selector: 'app-shared-upload-file',
|
||||||
@@ -13,11 +13,11 @@ export class SharedUploadFileComponent {
|
|||||||
@Input() loading: boolean = false;
|
@Input() loading: boolean = false;
|
||||||
@Input() name: string = 'file';
|
@Input() name: string = 'file';
|
||||||
@Input() accept: string = 'image/*';
|
@Input() accept: string = 'image/*';
|
||||||
@Input() maxSize: Maybe<number> = 5 * 1024;
|
@Input() maxSize: Maybe<number> = 5 * 1024 * 1024;
|
||||||
@Input() error: Maybe<string> = null;
|
@Input() error: Maybe<string> = null;
|
||||||
@Input() hint: Maybe<string> = null;
|
@Input() hint: Maybe<string> = null;
|
||||||
|
|
||||||
@Output() onSelect = new EventEmitter<onSelectArgs>();
|
@Output() onSelect = new EventEmitter<onSelectFileArgs>();
|
||||||
|
|
||||||
onSelectedFile($event: FileSelectEvent) {
|
onSelectedFile($event: FileSelectEvent) {
|
||||||
const file = $event.files?.[0];
|
const file = $event.files?.[0];
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Maybe } from '@/core';
|
||||||
|
import { apiEnumTranslates } from '@/shared/apiEnum/constants';
|
||||||
|
|
||||||
|
const translates = {
|
||||||
|
COUNT: 'تعداد',
|
||||||
|
GRAM: 'گرم',
|
||||||
|
KILOGRAM: 'کیلوگرم',
|
||||||
|
METER: 'متر',
|
||||||
|
MILLILITER: 'میلیلیتر',
|
||||||
|
LITER: 'لیتر',
|
||||||
|
HOUR: 'ساعت',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function enumTranslator(key: Maybe<string>): string {
|
||||||
|
if (!key) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
return apiEnumTranslates[key] || translates[key] || key;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export function buildFormData(
|
||||||
|
formGroup: any,
|
||||||
|
fileProperties: { [key: string]: File | null } = {},
|
||||||
|
): FormData {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
console.log(formGroup);
|
||||||
|
console.log(formGroup.controls);
|
||||||
|
|
||||||
|
Object.keys(formGroup.controls).forEach((key) => {
|
||||||
|
const control = formGroup.get(key);
|
||||||
|
if (control && control.value !== null && control.value !== undefined) {
|
||||||
|
formData.append(key, control.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(fileProperties).forEach((key) => {
|
||||||
|
const file = fileProperties[key];
|
||||||
|
if (file) {
|
||||||
|
formData.append(key, file, file.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
export * from './currency';
|
export * from './currency';
|
||||||
export * from './date-formatter.utils';
|
export * from './date-formatter.utils';
|
||||||
|
export * from './enum-translator.utils';
|
||||||
|
export * from './form-data.util';
|
||||||
export * from './good-unit-types.utils';
|
export * from './good-unit-types.utils';
|
||||||
|
export * from './license-status.utils';
|
||||||
export * from './name';
|
export * from './name';
|
||||||
export * from './page-params.utils';
|
export * from './page-params.utils';
|
||||||
export * from './price-mask.utils';
|
export * from './price-mask.utils';
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Maybe } from '@/core';
|
||||||
|
import { licenseStatus } from '@/shared/localEnum/constants/licenseStatus';
|
||||||
|
|
||||||
|
export function getLicenseStatus(licenseExpiresAt?: Maybe<string>) {
|
||||||
|
if (!licenseExpiresAt) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (new Date().getTime() > new Date(licenseExpiresAt || '').getTime()) {
|
||||||
|
return licenseStatus.EXPIRED;
|
||||||
|
}
|
||||||
|
return licenseStatus.ACTIVE;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user