Files
psp_panel/src/app/modules/pos/store/index.ts
T

273 lines
7.9 KiB
TypeScript
Raw Normal View History

import { Maybe } from '@/core';
2026-03-10 13:36:45 +03:30
// import { ICustomerResponse } from '@/modules/customers/models';
import { computed, Inject, Injectable, InjectionToken, signal } from '@angular/core';
import { map, of } from 'rxjs';
import { IPosInfoResponse, IPosProductCategoriesResponse, IPosStockResponse } from '../models';
import { IPosInOrderProduct } from '../models/types';
import { PosService } from '../services/main.service';
export const POS_ID = new InjectionToken<number>('POS_ID');
export type TViewType = 'list' | 'grid';
interface IPosState {
getStockLoading: boolean;
stock: Maybe<IPosStockResponse[]>;
getInfoLoading: boolean;
info: Maybe<IPosInfoResponse>;
getProductCategoriesLoading: boolean;
productCategories: Maybe<IPosProductCategoriesResponse[]>;
activeProductCategory: Maybe<number>;
inOrderProducts: IPosInOrderProduct[];
2026-03-10 13:36:45 +03:30
selectedCustomer: Maybe<any>;
viewType: TViewType;
searchQuery: string;
}
export const INITIAL_POS_STATE: IPosState = {
getStockLoading: true,
stock: null,
getInfoLoading: true,
info: null,
getProductCategoriesLoading: true,
productCategories: null,
activeProductCategory: null,
inOrderProducts: [],
selectedCustomer: null,
viewType: 'grid',
searchQuery: '',
};
@Injectable({ providedIn: 'any' })
export class POSStore {
constructor(
@Inject(POS_ID) posId: number,
private service: PosService,
) {
this.posId = posId;
this.initial();
}
private state$ = signal<IPosState>({ ...INITIAL_POS_STATE });
readonly stock = computed(() => this.state$().stock);
readonly getStockLoading = computed(() => this.state$().getStockLoading);
readonly info = computed(() => this.state$().info);
readonly getInfoLoading = computed(() => this.state$().getInfoLoading);
readonly inOrderProducts = computed(() => this.state$().inOrderProducts);
readonly productCategories = computed(() => this.state$().productCategories);
readonly getProductCategoriesLoading = computed(() => this.state$().getProductCategoriesLoading);
readonly activeProductCategory = computed(() => this.state$().activeProductCategory);
readonly selectedCustomer = computed(() => this.state$().selectedCustomer);
readonly viewType = computed(() => this.state$().viewType);
readonly searchQuery = computed(() => this.state$().searchQuery);
readonly activatedCategoryProducts = computed(() => {
if (this.activeProductCategory() === 0) {
return this.state$().stock;
}
return this.state$().stock?.filter(
(s) => s.product.categoryId === this.state$().activeProductCategory,
);
});
readonly orderPricingInfo = computed(() => {
const totalAmount = this.inOrderProducts().reduce(
(acc, curr) => acc + curr.unitPrice * curr.count,
0,
);
const taxAmount = totalAmount * 0.1;
return {
totalItems: this.inOrderProducts().reduce((acc, curr) => acc + curr.count, 0),
totalBaseAmount: this.inOrderProducts().reduce(
(acc, curr) => acc + parseFloat(curr.product.salePrice) * curr.count,
0,
),
totalAmount,
taxAmount,
payableAmount: totalAmount + taxAmount,
};
});
posId!: number;
setState(partial: Partial<IPosState>) {
this.state$.set({ ...this.state$(), ...partial });
}
reset() {
this.state$.set({ ...INITIAL_POS_STATE });
}
initial() {
this.getInfo();
this.getStock();
this.getProductCategories();
}
fillInitial(data: Partial<IPosState>) {
this.state$.set({ ...INITIAL_POS_STATE, ...data });
}
getInfo() {
this.setState({ getInfoLoading: true });
this.service.getInfo(this.posId).subscribe({
next: (res) => {
this.setState({ info: res, getInfoLoading: false });
},
error: () => {
this.setState({ getInfoLoading: false });
},
});
}
getStock() {
this.setState({ getStockLoading: true });
this.service.getStock(this.posId, this.state$().searchQuery).subscribe({
next: (res) => {
this.setState({ stock: res.data, getStockLoading: false });
},
error: () => {
this.setState({ getStockLoading: false });
},
});
}
getProductCategories() {
this.setState({ getProductCategoriesLoading: true });
this.service
.getCategories(this.posId)
.pipe(
map((res) => {
return {
data: [
{
id: 0,
name: 'همه',
totalQuantity: res.data.reduce((acc, curr) => acc + curr.totalQuantity, 0),
productCount: res.data.reduce((acc, curr) => acc + curr.productCount, 0),
},
...res.data,
],
meta: res.meta,
};
}),
)
.subscribe({
next: (res) => {
this.setState({
productCategories: res.data,
getProductCategoriesLoading: false,
activeProductCategory: res.data[0]?.id,
});
},
error: () => {
this.setState({ getProductCategoriesLoading: false });
},
});
}
changeActiveCategory(categoryId: number) {
this.setState({ activeProductCategory: categoryId });
}
addToInOrderProducts(productId: number) {
const productStock = this.state$().stock?.find((s) => s.product.id === productId);
if (!productStock) return;
const { product } = productStock;
if (this.state$().inOrderProducts.some((p) => p.productId === productId)) {
this.updateInOrderProductQuantity(
productId,
this.state$().inOrderProducts.find((p) => p.productId === productId)!.count + 1,
);
} else {
this.setState({
inOrderProducts: [
...this.state$().inOrderProducts,
{
product,
productId: product.id,
count: 1,
unitPrice: parseFloat(product.salePrice),
maxQuantity: productStock.quantity,
},
],
});
}
}
removeFromInOrderProducts(productId: number) {
this.setState({
inOrderProducts: this.state$().inOrderProducts.filter((p) => p.productId !== productId),
});
}
updateInOrderProductQuantity(productId: number, quantity: number) {
const inOrderProducts = this.state$().inOrderProducts;
if (quantity < 1) {
this.removeFromInOrderProducts(productId);
} else if (inOrderProducts.findIndex((p) => p.productId === productId) === -1) {
this.addToInOrderProducts(productId);
} else {
const updatedProducts = inOrderProducts.map((p) => {
if (p.productId === productId) {
return { ...p, count: Math.min(quantity, p.maxQuantity) };
}
return p;
});
this.setState({ inOrderProducts: updatedProducts });
}
}
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, unitPrice: unitPrice };
}
return p;
});
this.setState({ inOrderProducts: updatedProducts });
}
}
updateViewType(viewType: TViewType) {
this.setState({ viewType });
}
updateSearchQuery(searchQuery: string) {
this.setState({ searchQuery });
}
resetInOrderProducts() {
this.setState({ inOrderProducts: [] });
}
2026-03-10 13:36:45 +03:30
setSelectedCustomer(customer: Maybe<any>) {
this.setState({ selectedCustomer: customer });
}
submitOrder() {
const orderPayload = {
2026-03-30 13:17:34 +03:30
customer_id: this.selectedCustomer()?.id,
items: this.inOrderProducts()?.map((item) => ({
productId: item.productId,
count: item.count,
unitPrice: item.unitPrice,
})),
};
return this.service.submitOrder(this.posId, orderPayload).subscribe({
next: (res) => {
this.resetInOrderProducts();
this.setSelectedCustomer(null);
return of(res);
},
error: (err) => {
return of(err);
},
});
}
}