/home/bdqbpbxa/dev-subdomains/admin.pixory.goodface.com.ua/src/api/project/services/project.ts
import { Core } from "@strapi/strapi";
import { ApplicationError } from '@strapi/utils/dist/errors';
import { round } from "src/api/utils/math";
import { Pricing } from "src/api/pricing/common/types/pricing";
import { getDiscountMultiplier } from "src/api/pricing/common/utils/discount-multiplier";
import { BookSettings } from "src/api/book-setting/common/types/book-settings";
import { BookType } from "src/api/book-type/common/types/book-type";
import { ShippingPrice } from "src/api/shipping-price/common/types/shipping-price";
import { PrintboxService } from "src/services/printbox/printbox.service";
import { PrintboxProject, ProductResponse, Project } from "../common/types/project";
export class ProjectService {
constructor(private readonly strapi: Core.Strapi) {}
private readonly bookTypeSignificanceLevels: {
hardcover: 0,
softcover: 1,
layFlat: 2
}
async update(id: number, data: Partial<Project>): Promise<ProductResponse> {
const updatedProject = await this.strapi.db.query('api::project.project').update({
where: { id },
data,
populate: {
product: true,
bookSize: true,
bookType: true,
paperFinish: true,
},
});
return this.transform(updatedProject);
}
async transform(project: Project): Promise<ProductResponse> {
const bookType = await this.strapi.db.query('api::book-type.book-type').findOne() as BookType;
const projectData = await this.getProjectData(project.printboxProjectUuid);
const pageCount = projectData.params[0]?.page_count || 0;
const defaultPages = bookType.minPages;
const additionalPages = pageCount - bookType.minPages;
const pricing = await this.getBookPricing(project);
if (!pricing) {
throw new ApplicationError(`Pricing not found for project type, size and paper finish. Project ID: ${project.id}`);
}
const defaultPagesPrice = await this.calculatePagesPrice(pricing, bookType.minPages);
const additionalPagesPrice = await this.calculatePagesPrice(pricing, additionalPages);
const discountedDefaultPagesPrice = pricing.discount ? defaultPagesPrice * getDiscountMultiplier(pricing.discount) : null;
const discountedAdditionalPagesPrice = pricing.discount ? additionalPagesPrice * getDiscountMultiplier(pricing.discount) : null;
const subtotal = defaultPagesPrice + additionalPagesPrice;
const subtotalDiscounted = pricing.discount ? discountedAdditionalPagesPrice + discountedDefaultPagesPrice : null;
const discount = subtotalDiscounted ? (subtotal - subtotalDiscounted) / subtotal * 100 : 0;
return {
...project,
defaultPages,
additionalPages,
defaultPagesPrice: round(defaultPagesPrice),
additionalPagesPrice: round(additionalPagesPrice),
discountedDefaultPagesPrice: discountedDefaultPagesPrice ? round(discountedDefaultPagesPrice) : null,
discountedAdditionalPagesPrice: discountedAdditionalPagesPrice ? round(discountedAdditionalPagesPrice) : null,
subtotal: round(subtotal),
subtotalDiscounted: subtotalDiscounted ? round(subtotalDiscounted) : null,
discount: round(discount),
};
}
async calculatePagesPrice (pricing: Pricing, payablePages: number) {
const projectPrice = pricing.base + (pricing.perPage * payablePages);
return projectPrice;
};
async getBookPricing (project: Project): Promise<Pricing> {
const bookSettings = await strapi.db.query('api::book-setting.book-setting').findOne({
where: {
bookType: project.bookType.id,
bookSize: project.bookSize.id,
paperFinish: project.paperFinish.id,
},
populate: {
pricing: true,
},
}) as BookSettings;
return bookSettings?.pricing;
}
async getHighestPageCount(projects: Project[]): Promise<number> {
const printboxProjects = await this.getProjectsData(projects.map(p => p.printboxProjectUuid));
return printboxProjects.reduce((max, projectData) => {
return Math.max(max, projectData.params[0]?.page_count || 0);
}, 0);
}
async getSignificantBookType(projects: Project[], highestPageCount: number): Promise<BookType> {
const shippingPrices = await this.fetchShippingPrices(projects, highestPageCount);
const filteredRegions = this.filterRegionsByBookCount(shippingPrices, projects.length);
const bookTypes = this.extractBookTypes(filteredRegions);
return this.getMostSignificantBookType(bookTypes);
}
private async fetchShippingPrices(projects: Project[], highestPageCount: number): Promise<ShippingPrice[]> {
return await this.strapi.db.query('api::shipping-price.shipping-price').findMany({
where: {
minPage: { $lte: highestPageCount },
maxPage: { $gte: highestPageCount },
bookType: { $in: projects.map(p => p.bookType.id) }
},
populate: {
shippingPrices: true,
bookType: true,
},
}) as ShippingPrice[];
}
private filterRegionsByBookCount(regions: ShippingPrice[], projectCount: number): ShippingPrice[] {
const exactMatchRegions = regions.filter(r => r.bookNumber === projectCount);
if (exactMatchRegions.length > 0) {
return exactMatchRegions;
}
const maxBookNumber = Math.max(...regions.map(r => r.bookNumber));
return regions.filter(r => r.bookNumber === maxBookNumber);
}
private extractBookTypes(regions: ShippingPrice[]): BookType[] {
return regions.map(r => r.bookType);
}
private getMostSignificantBookType(bookTypes: BookType[]): BookType {
const sortedBookTypes = bookTypes.sort((a, b) => {
return this.bookTypeSignificanceLevels[a.title] - this.bookTypeSignificanceLevels[b.title];
});
return sortedBookTypes[0];
}
async getProjectData(projectId: string): Promise<PrintboxProject> {
const printboxService = new PrintboxService();
return await printboxService.getProject(projectId);
}
async getProjectsData(projectId: string[]): Promise<PrintboxProject[]> {
const projectsData = projectId.map(async (id) => await this.getProjectData(id));
return Promise.all(projectsData);
}
}