219 lines
6.4 KiB
TypeScript
219 lines
6.4 KiB
TypeScript
|
|
import { Maybe } from '@/core';
|
||
|
|
import { computed, Injectable, signal } from '@angular/core';
|
||
|
|
import { map } from 'rxjs';
|
||
|
|
import { IPosInfoResponse, IPosProductCategoriesResponse, IPosStockResponse } from '../models';
|
||
|
|
import { IPosInOrderProduct } from '../models/types';
|
||
|
|
import { PosService } from '../services/main.service';
|
||
|
|
|
||
|
|
interface IPosState {
|
||
|
|
getStockLoading: boolean;
|
||
|
|
stock: Maybe<IPosStockResponse[]>;
|
||
|
|
getInfoLoading: boolean;
|
||
|
|
info: Maybe<IPosInfoResponse>;
|
||
|
|
getProductCategoriesLoading: boolean;
|
||
|
|
productCategories: Maybe<IPosProductCategoriesResponse[]>;
|
||
|
|
activeProductCategory: Maybe<number>;
|
||
|
|
inOrderProducts: IPosInOrderProduct[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export const INITIAL_POS_STATE: IPosState = {
|
||
|
|
getStockLoading: true,
|
||
|
|
stock: null,
|
||
|
|
getInfoLoading: true,
|
||
|
|
info: null,
|
||
|
|
getProductCategoriesLoading: true,
|
||
|
|
productCategories: null,
|
||
|
|
activeProductCategory: null,
|
||
|
|
inOrderProducts: [],
|
||
|
|
};
|
||
|
|
|
||
|
|
@Injectable({ providedIn: 'any' })
|
||
|
|
export class POSStore {
|
||
|
|
constructor(private service: PosService) {}
|
||
|
|
|
||
|
|
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 activatedCategoryProducts = computed(() => {
|
||
|
|
if (this.activeProductCategory() === 0) {
|
||
|
|
return this.state$().stock;
|
||
|
|
}
|
||
|
|
return this.state$().stock?.filter(
|
||
|
|
(s) => s.product.category.id === this.state$().activeProductCategory,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
readonly orderPricingInfo = computed(() => {
|
||
|
|
const totalAmount = this.inOrderProducts().reduce(
|
||
|
|
(acc, curr) => acc + parseFloat(curr.fee) * curr.quantity,
|
||
|
|
0,
|
||
|
|
);
|
||
|
|
const taxAmount = totalAmount * 0.1;
|
||
|
|
return {
|
||
|
|
totalItems: this.inOrderProducts().reduce((acc, curr) => acc + curr.quantity, 0),
|
||
|
|
totalBaseAmount: this.inOrderProducts().reduce(
|
||
|
|
(acc, curr) => acc + parseFloat(curr.product.salePrice) * curr.quantity,
|
||
|
|
0,
|
||
|
|
),
|
||
|
|
totalAmount,
|
||
|
|
taxAmount,
|
||
|
|
payableAmount: totalAmount + taxAmount,
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
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().subscribe({
|
||
|
|
next: (res) => {
|
||
|
|
this.setState({ info: res, getInfoLoading: false });
|
||
|
|
},
|
||
|
|
error: () => {
|
||
|
|
this.setState({ getInfoLoading: false });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
getStock() {
|
||
|
|
this.setState({ getStockLoading: true });
|
||
|
|
this.service.getStock().subscribe({
|
||
|
|
next: (res) => {
|
||
|
|
this.setState({ stock: res.data, getStockLoading: false });
|
||
|
|
},
|
||
|
|
error: () => {
|
||
|
|
this.setState({ getStockLoading: false });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
getProductCategories() {
|
||
|
|
this.setState({ getProductCategoriesLoading: true });
|
||
|
|
this.service
|
||
|
|
.getCategories()
|
||
|
|
.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)!.quantity + 1,
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
this.setState({
|
||
|
|
inOrderProducts: [
|
||
|
|
...this.state$().inOrderProducts,
|
||
|
|
{
|
||
|
|
product,
|
||
|
|
productId: product.id,
|
||
|
|
quantity: 1,
|
||
|
|
fee: 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, quantity: Math.min(quantity, p.maxQuantity) };
|
||
|
|
}
|
||
|
|
return p;
|
||
|
|
});
|
||
|
|
this.setState({ inOrderProducts: updatedProducts });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
updateInOrderProductFee(productId: number, fee: string) {
|
||
|
|
const inOrderProducts = this.state$().inOrderProducts;
|
||
|
|
|
||
|
|
if (inOrderProducts.some((p) => p.productId === productId)) {
|
||
|
|
const updatedProducts = inOrderProducts.map((p) => {
|
||
|
|
if (p.productId === productId) {
|
||
|
|
return { ...p, fee: fee };
|
||
|
|
}
|
||
|
|
return p;
|
||
|
|
});
|
||
|
|
this.setState({ inOrderProducts: updatedProducts });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
resetInOrderProducts() {
|
||
|
|
this.setState({ inOrderProducts: [] });
|
||
|
|
}
|
||
|
|
}
|