UNPKG

@rsc-labs/medusa-store-analytics-v2

Version:
324 lines 29.2 kB
"use strict"; /* * Copyright 2024 RSC-Labs, https://rsoftcon.com/ * * MIT License * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.OrdersAnalyticsService = void 0; const dateTransformations_1 = require("./../utils/dateTransformations"); class OrdersAnalyticsService { constructor({ __pg_connection__ }) { this.pgConnection = __pg_connection__; } async getOrdersHistory(orderStatuses, from, to, dateRangeFromCompareTo, dateRangeToCompareTo) { const orderStatusesAsStrings = Object.values(orderStatuses); if (orderStatusesAsStrings.length) { if (dateRangeFromCompareTo && from && to && dateRangeToCompareTo) { const resolution = (0, dateTransformations_1.calculateResolution)(from); const orders = await this.pgConnection('order') .select(this.pgConnection.raw(` CASE WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'previous' WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'current' END AS type, date_trunc(?, "order".created_at) AS date `, [dateRangeToCompareTo, dateRangeFromCompareTo, to, from, resolution])) .count('order.id AS orderCount') .where('order.created_at', '>=', dateRangeFromCompareTo) .andWhere('order.status', 'IN', orderStatusesAsStrings) .groupBy(['type', 'date']) .orderByRaw('"date" ASC, "type" ASC'); const theOrders = orders; const finalOrders = theOrders.reduce((acc, entry) => { const { type, date, orderCount } = entry; if (!acc[type]) { acc[type] = []; } acc[type].push({ date, orderCount }); return acc; }, {}); return { dateRangeFrom: from.getTime(), dateRangeTo: to.getTime(), dateRangeFromCompareTo: dateRangeFromCompareTo.getTime(), dateRangeToCompareTo: dateRangeToCompareTo.getTime(), current: finalOrders.current ? finalOrders.current : [], previous: finalOrders.previous ? finalOrders.previous : [], }; } let startQueryFrom; if (!dateRangeFromCompareTo) { if (from) { startQueryFrom = from; } else { const lastOrder = await this.pgConnection('order') .select('created_at') .whereIn('status', orderStatusesAsStrings) .orderBy('created_at', 'ASC') .limit(1) .then(result => result[0]); if (lastOrder) { startQueryFrom = lastOrder.created_at; } } } else { startQueryFrom = dateRangeFromCompareTo; } if (startQueryFrom) { const endQuery = to ? to : new Date(Date.now()); const resolution = (0, dateTransformations_1.calculateResolution)(startQueryFrom, endQuery); const rawOrders = await this.pgConnection('order') .select(this.pgConnection.raw(`date_trunc(?, "order".created_at) AS date`, [resolution])) .count('order.id AS orderCount') .where('order.created_at', '>=', startQueryFrom) .andWhere('order.created_at', '<=', endQuery) .andWhere('order.status', 'IN', orderStatusesAsStrings) .groupBy('date') .orderBy('date', 'ASC'); const orders = rawOrders; return { dateRangeFrom: startQueryFrom.getTime(), dateRangeTo: endQuery.getTime(), dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: orders, previous: [] }; } } return { dateRangeFrom: undefined, dateRangeTo: undefined, dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: [], previous: [] }; } async getOrdersCount(orderStatuses, from, to, dateRangeFromCompareTo, dateRangeToCompareTo) { let startQueryFrom; const orderStatusesAsStrings = Object.values(orderStatuses); if (orderStatusesAsStrings.length) { if (dateRangeFromCompareTo && from && to && dateRangeToCompareTo) { const orders = await this.pgConnection .select([ 'id', 'created_at', 'updated_at', 'customer_id', ]) .select(this.pgConnection.raw(`CASE WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'previous' WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'current' END AS type `, [dateRangeToCompareTo, dateRangeFromCompareTo, to, from])) .from('order') .whereIn('status', orderStatusesAsStrings) .andWhere('order.created_at', '>=', dateRangeFromCompareTo) .groupBy(['type', 'id']) .orderBy([{ column: 'type', order: 'ASC' }]) .then(result => result); const previousOrders = orders.filter(order => order.type == 'previous'); const currentOrders = orders.filter(order => order.type == 'current'); return { dateRangeFrom: from.getTime(), dateRangeTo: to.getTime(), dateRangeFromCompareTo: dateRangeFromCompareTo.getTime(), dateRangeToCompareTo: dateRangeToCompareTo.getTime(), current: currentOrders.length, previous: previousOrders.length }; } if (!dateRangeFromCompareTo) { if (from) { startQueryFrom = from; } else { const lastOrder = await this.pgConnection('order') .select('created_at') .whereIn('status', orderStatusesAsStrings) .orderBy('created_at', 'ASC') .limit(1) .then(result => result[0]); if (lastOrder) { startQueryFrom = lastOrder.created_at; } } } else { startQueryFrom = dateRangeFromCompareTo; } if (startQueryFrom) { const endQuery = to ? to : new Date(Date.now()); const orders = await this.pgConnection('order') .select([ 'id', 'created_at', 'updated_at', 'customer_id', ]) .whereIn('status', orderStatusesAsStrings) .andWhere('created_at', '>=', startQueryFrom) .andWhere('created_at', '<=', endQuery) .orderBy('created_at', 'DESC') .then(result => result); return { dateRangeFrom: startQueryFrom.getTime(), dateRangeTo: to ? to.getTime() : new Date(Date.now()).getTime(), dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: orders.length, previous: 0 }; } } return { dateRangeFrom: undefined, dateRangeTo: undefined, dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: 0, previous: 0 }; } async getPaymentProviderPopularity(from, to, dateRangeFromCompareTo, dateRangeToCompareTo) { function calculateSumAndPercentageOfResults(results) { const orderMap = new Map(); let allSum = 0; results.forEach(result => { const { orderCount, paymentProviderId } = result; if (orderMap.has(paymentProviderId)) { const sum = parseInt(orderMap.get(paymentProviderId)) + parseInt(orderCount); orderMap.set(paymentProviderId, sum.toFixed()); } else { orderMap.set(paymentProviderId, orderCount); } }); const newArray = []; orderMap.forEach((value) => { allSum += parseInt(value); }); orderMap.forEach((value, key) => { newArray.push({ orderCount: value, percentage: (parseInt(value) * 100 / allSum).toFixed(2), paymentProviderId: key }); }); return newArray; } if (dateRangeFromCompareTo && from && to && dateRangeToCompareTo) { const resolution = (0, dateTransformations_1.calculateResolution)(from); const rawOrdersCountWithPayments = await this.pgConnection('order') .select(this.pgConnection.raw(` CASE WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'previous' WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'current' END AS type, date_trunc(?, "order".created_at) AS date `, [dateRangeToCompareTo, dateRangeFromCompareTo, to, from, resolution])) .count('order.id AS orderCount') .select('payment.provider_id AS paymentProviderId') .innerJoin('order_payment_collection', 'order.id', 'order_payment_collection.order_id') .innerJoin('payment_collection', 'order_payment_collection.payment_collection_id', 'payment_collection.id') .innerJoin('payment', 'payment.payment_collection_id', 'payment_collection.id') .where('order.created_at', '>=', dateRangeFromCompareTo) .groupByRaw(`date, type, payment.provider_id`) .orderBy('date', 'ASC'); const ordersCountWithPayments = rawOrdersCountWithPayments; const finalOrders = ordersCountWithPayments.reduce((acc, entry) => { const type = entry.type; const orderCount = entry.orderCount; const paymentProviderId = entry.paymentProviderId; if (!acc[type]) { acc[type] = []; } acc[type].push({ orderCount, paymentProviderId, }); return acc; }, {}); const finalOrdersCurrentGrouped = calculateSumAndPercentageOfResults(finalOrders.current ? finalOrders.current : []); const finalOrdersPreviousGrouped = calculateSumAndPercentageOfResults(finalOrders.previous ? finalOrders.previous : []); return { dateRangeFrom: from.getTime(), dateRangeTo: to.getTime(), dateRangeFromCompareTo: dateRangeFromCompareTo.getTime(), dateRangeToCompareTo: dateRangeToCompareTo.getTime(), current: finalOrdersCurrentGrouped ? finalOrdersCurrentGrouped : [], previous: finalOrdersPreviousGrouped ? finalOrdersPreviousGrouped : [], }; } let startQueryFrom; if (!dateRangeFromCompareTo) { if (from) { startQueryFrom = from; } else { const lastOrder = await this.pgConnection('order') .select('created_at') .orderBy('created_at', 'ASC') .limit(1) .then(result => result[0]); if (lastOrder) { startQueryFrom = lastOrder.created_at; } } } else { startQueryFrom = dateRangeFromCompareTo; } if (startQueryFrom) { const endQuery = to ? to : new Date(Date.now()); const resolution = (0, dateTransformations_1.calculateResolution)(startQueryFrom, endQuery); const rawOrdersCountWithPayments = await this.pgConnection('order') .select(this.pgConnection.raw(`date_trunc(?, "order".created_at) AS date`, [resolution])) .count('order.id AS orderCount') .select('payment.provider_id AS paymentProviderId') .innerJoin('order_payment_collection', 'order.id', 'order_payment_collection.order_id') .innerJoin('payment_collection', 'order_payment_collection.payment_collection_id', 'payment_collection.id') .innerJoin('payment', 'payment.payment_collection_id', 'payment_collection.id') .where('order.created_at', '>=', startQueryFrom) .andWhere('order.created_at', '<=', endQuery) .groupByRaw(`date, payment.provider_id`) .orderBy('date', 'ASC'); const ordersCountWithPayments = rawOrdersCountWithPayments; const initialOrders = ordersCountWithPayments.map(order => { return { orderCount: order.orderCount, paymentProviderId: order.paymentProviderId, }; }); const finalOrdersGrouped = calculateSumAndPercentageOfResults(initialOrders ? initialOrders : []); return { dateRangeFrom: startQueryFrom.getTime(), dateRangeTo: to ? to.getTime() : new Date(Date.now()).getTime(), dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: finalOrdersGrouped, previous: [] }; } return { dateRangeFrom: undefined, dateRangeTo: undefined, dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: [], previous: [] }; } } exports.OrdersAnalyticsService = OrdersAnalyticsService; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JkZXJzQW5hbHl0aWNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvc3RvcmUtYW5hbHl0aWNzL3NlcnZpY2VzL29yZGVyc0FuYWx5dGljcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7R0FVRzs7O0FBR0gsd0VBQW9FO0FBbURwRSxNQUFhLHNCQUFzQjtJQUlqQyxZQUFZLEVBQUUsaUJBQWlCLEVBQXdCO1FBQ3JELElBQUksQ0FBQyxZQUFZLEdBQUcsaUJBQWlCLENBQUE7SUFDdkMsQ0FBQztJQUVELEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxhQUE0QixFQUFFLElBQVcsRUFBRSxFQUFTLEVBQUUsc0JBQTZCLEVBQUUsb0JBQTJCO1FBQ3JJLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUM1RCxJQUFJLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2xDLElBQUksc0JBQXNCLElBQUksSUFBSSxJQUFJLEVBQUUsSUFBSSxvQkFBb0IsRUFBRSxDQUFDO2dCQUNqRSxNQUFNLFVBQVUsR0FBRyxJQUFBLHlDQUFtQixFQUFDLElBQUksQ0FBQyxDQUFDO2dCQUM3QyxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDO3FCQUM1QyxNQUFNLENBQ0wsSUFBSSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUM7Ozs7OzthQU1yQixFQUFFLENBQUMsb0JBQW9CLEVBQUUsc0JBQXNCLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxVQUFVLENBQUMsQ0FBQyxDQUN6RTtxQkFDQSxLQUFLLENBQUMsd0JBQXdCLENBQUM7cUJBQy9CLEtBQUssQ0FBQyxrQkFBa0IsRUFBRSxJQUFJLEVBQUUsc0JBQXNCLENBQUM7cUJBQ3ZELFFBQVEsQ0FBQyxjQUFjLEVBQUUsSUFBSSxFQUFFLHNCQUFzQixD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