UNPKG

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

Version:
436 lines 41.6 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.SalesAnalyticsService = void 0; const dateTransformations_1 = require("./../utils/dateTransformations"); const currency_1 = require("./utils/currency"); function groupPerDate(orders, resolution, decimalMultiplier) { const funcTruncateDate = (0, dateTransformations_1.getTruncateFunction)(resolution); return orders.reduce((accumulator, order) => { const truncatedDate = funcTruncateDate(order.created_at); if (!accumulator[truncatedDate.toISOString()]) { if (resolution == dateTransformations_1.DateResolutionType.Day) { accumulator[truncatedDate.toISOString()] = { date: new Date(new Date(order.created_at).setHours(0, 0, 0, 0)), total: 0 }; } else { accumulator[truncatedDate.toISOString()] = { date: new Date(new Date(new Date(order.created_at).setDate(1)).setHours(0, 0, 0, 0)), total: 0 }; } } accumulator[truncatedDate.toISOString()].total += order.totals.original_order_total * Math.pow(10, decimalMultiplier); return accumulator; }, {}); } class SalesAnalyticsService { constructor({ __pg_connection__ }) { this.pgConnection = __pg_connection__; } async getOrdersSales(orderStatuses, currencyCode, from, to, dateRangeFromCompareTo, dateRangeToCompareTo) { let startQueryFrom; const orderStatusesAsStrings = Object.values(orderStatuses); const decimalDigits = (0, currency_1.getDecimalDigits)(currencyCode); if (orderStatusesAsStrings.length) { if (!dateRangeFromCompareTo) { if (from) { startQueryFrom = from; } else { const lastOrder = await this.pgConnection('order') .select('created_at') .whereIn('status', orderStatusesAsStrings) .andWhere('currency_code', currencyCode) .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 ordersWithSummary = await this.pgConnection('order') .select([ 'order.*', 'order_summary.id as summary_id', 'order_summary.totals', 'order_summary.created_at as summary_created_at', 'order_summary.updated_at as summary_updated_at' ]) .leftJoin('order_summary', 'order.id', 'order_summary.order_id') .whereIn('order.status', orderStatusesAsStrings) .andWhere('order.currency_code', currencyCode) .andWhere('order.created_at', '>=', startQueryFrom) .andWhere('order.created_at', '<=', endQuery) .orderBy('order.created_at', 'DESC') .then(result => result); const uniqueOrdersWithSummary = Array.from(new Map(ordersWithSummary.map(order => [order.id, order])).values()); if (dateRangeFromCompareTo && from && to && dateRangeToCompareTo) { const previousOrders = uniqueOrdersWithSummary.filter(order => order.created_at < from); const currentOrders = uniqueOrdersWithSummary.filter(order => order.created_at >= from); const resolution = (0, dateTransformations_1.calculateResolution)(from); const groupedCurrentOrders = groupPerDate(currentOrders, resolution, decimalDigits); const groupedPreviousOrders = groupPerDate(previousOrders, resolution, decimalDigits); const currentSales = Object.values(groupedCurrentOrders); const previousSales = Object.values(groupedPreviousOrders); return { dateRangeFrom: from.getTime(), dateRangeTo: to.getTime(), dateRangeFromCompareTo: dateRangeFromCompareTo.getTime(), dateRangeToCompareTo: dateRangeToCompareTo.getTime(), currencyCode: currencyCode, currencyDecimalDigits: decimalDigits, current: currentSales.sort((a, b) => a.date.getTime() - b.date.getTime()), previous: previousSales.sort((a, b) => a.date.getTime() - b.date.getTime()) }; } const resolution = (0, dateTransformations_1.calculateResolution)(startQueryFrom); const currentOrders = uniqueOrdersWithSummary; const groupedCurrentOrders = groupPerDate(currentOrders, resolution, decimalDigits); const currentSales = Object.values(groupedCurrentOrders); return { dateRangeFrom: startQueryFrom.getTime(), dateRangeTo: to ? to.getTime() : new Date(Date.now()).getTime(), dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, currencyCode: currencyCode, currencyDecimalDigits: (0, currency_1.getDecimalDigits)(currencyCode), current: currentSales.sort((a, b) => a.date.getTime() - b.date.getTime()), previous: [] }; } } return { dateRangeFrom: undefined, dateRangeTo: undefined, dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, currencyCode: currencyCode, currencyDecimalDigits: (0, currency_1.getDecimalDigits)(currencyCode), current: [], previous: [] }; } async getSalesChannelsPopularity(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 rawOrdersCountBySalesChannel = await this.pgConnection('order') .select([ this.pgConnection.raw(` CASE WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'previous' ELSE 'current' END AS type `, [from, dateRangeFromCompareTo]), this.pgConnection.raw(`date_trunc(?, "order".created_at) AS date`, [resolution]), 'sales_channel.id as sales_channel_id', 'sales_channel.name as sales_channel_name', this.pgConnection.raw(`COUNT("order".id) AS orderCount`) ]) .leftJoin('sales_channel', 'sales_channel.id', '=', 'order.sales_channel_id') .where('order.created_at', '>=', dateRangeFromCompareTo) .whereIn('order.status', orderStatusesAsStrings) .groupByRaw('type, date, sales_channel.id') .orderBy('date', 'ASC'); const ordersCountBySalesChannel = rawOrdersCountBySalesChannel; const finalOrders = ordersCountBySalesChannel.reduce((acc, entry) => { const type = entry.type; const date = entry.date; const orderCount = entry.ordercount; const salesChannelId = entry.sales_channel_id; const salesChannelName = entry.sales_channel_name; if (!acc[type]) { acc[type] = []; } acc[type].push({ date, orderCount, salesChannelId, salesChannelName }); 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 rawOrdersCountBySalesChannel = await this.pgConnection('order') .select([ this.pgConnection.raw(`date_trunc(?, "order".created_at) AS date`, [resolution]), 'sales_channel.id as sales_channel_id', 'sales_channel.name as sales_channel_name', this.pgConnection.raw(`COUNT("order".id) AS orderCount`) ]) .leftJoin('sales_channel', 'sales_channel.id', '=', 'order.sales_channel_id') .where('order.created_at', '>=', startQueryFrom) .andWhere('order.created_at', '<=', endQuery) .whereIn('order.status', orderStatusesAsStrings) .groupByRaw('date, sales_channel.id') .orderBy('date', 'ASC'); const ordersCountBySalesChannel = rawOrdersCountBySalesChannel; const finalOrders = ordersCountBySalesChannel.map(order => { return { date: order.date, orderCount: order.ordercount, salesChannelId: order.sales_channel_id, salesChannelName: order.sales_channel_name }; }); return { dateRangeFrom: startQueryFrom.getTime(), dateRangeTo: to ? to.getTime() : new Date(Date.now()).getTime(), dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: finalOrders, previous: [] }; } } return { dateRangeFrom: undefined, dateRangeTo: undefined, dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: [], previous: [] }; } async getRegionsPopularity(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 rawOrdersCountByRegion = await this.pgConnection('order') .select([ this.pgConnection.raw(` CASE WHEN "order".created_at < ? AND "order".created_at >= ? THEN 'previous' ELSE 'current' END AS type `, [from, dateRangeFromCompareTo]), this.pgConnection.raw(`date_trunc(?, "order".created_at) AS date`, [resolution]), this.pgConnection.raw(`COUNT("order".id) AS "orderCount"`), this.pgConnection.raw(`"region"."id" AS "region_id"`), this.pgConnection.raw(`"region"."name" AS "region_name"`) ]) .leftJoin('region', 'region.id', 'order.region_id') .where('order.created_at', '>=', dateRangeFromCompareTo) .whereIn('order.status', orderStatusesAsStrings) .groupBy(['date', 'type', 'region.id']) .orderBy('date', 'ASC'); const ordersCountByRegion = rawOrdersCountByRegion; const finalOrders = ordersCountByRegion.reduce((acc, entry) => { const type = entry.type; const date = entry.date; const orderCount = entry.orderCount; const regionId = entry.region_id; const regionName = entry.region_name; if (!acc[type]) { acc[type] = []; } acc[type].push({ date, orderCount, regionId, regionName }); 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); const rawOrdersCountByRegion = await this.pgConnection('order') .select([ this.pgConnection.raw(`date_trunc(?, "order".created_at) AS date`, [resolution]), 'region.id as region_id', 'region.name as region_name', this.pgConnection.raw(`COUNT("order".id) AS orderCount`) ]) .leftJoin('region', 'region.id', '=', 'order.region_id') .where('order.created_at', '>=', startQueryFrom) .andWhere('order.created_at', '<=', endQuery) .whereIn('order.status', orderStatusesAsStrings) .groupByRaw('date, region.id') .orderBy('date', 'ASC'); const ordersCountByRegion = rawOrdersCountByRegion; const finalOrders = ordersCountByRegion.map(order => { return { date: order.date, orderCount: order.ordercount, regionId: order.region_id, regionName: order.region_name }; }); return { dateRangeFrom: startQueryFrom.getTime(), dateRangeTo: to ? to.getTime() : new Date(Date.now()).getTime(), dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: finalOrders, previous: [] }; } } return { dateRangeFrom: undefined, dateRangeTo: undefined, dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: [], previous: [] }; } async getRefunds(currencyCode, from, to, dateRangeFromCompareTo, dateRangeToCompareTo) { if (dateRangeFromCompareTo && from && to && dateRangeToCompareTo) { const rawRefunds = await this.pgConnection('refund') .select([ this.pgConnection.raw(` CASE WHEN "refund"."created_at" < ? AND "refund"."created_at" >= ? THEN 'previous' ELSE 'current' END AS type `, [from, dateRangeFromCompareTo]), this.pgConnection.raw('SUM("refund"."amount") AS "sum"') ]) .innerJoin('payment', 'refund.payment_id', 'payment.id') .where('refund.created_at', '>=', dateRangeFromCompareTo) .andWhere('payment.currency_code', currencyCode) .groupByRaw('type'); const refunds = rawRefunds; const currentRefunds = refunds.find(refund => refund.type == 'current'); const previousRefunds = refunds.find(refund => refund.type == 'previous'); return { currencyCode: currencyCode, currencyDecimalDigits: (0, currency_1.getDecimalDigits)(currencyCode), dateRangeFrom: from.getTime(), dateRangeTo: to.getTime(), dateRangeFromCompareTo: dateRangeFromCompareTo.getTime(), dateRangeToCompareTo: dateRangeToCompareTo.getTime(), current: currentRefunds !== undefined ? currentRefunds.sum : '0', previous: previousRefunds !== undefined ? previousRefunds.sum : '0' }; } let startQueryFrom; if (!dateRangeFromCompareTo) { if (from) { startQueryFrom = from; } else { // All time const lastRefund = await this.pgConnection('refund') .select('created_at') .orderBy('created_at', 'ASC') .limit(1); if (lastRefund.length > 0) { startQueryFrom = lastRefund[0].created_at; } } } else { startQueryFrom = dateRangeFromCompareTo; } if (startQueryFrom) { const endQuery = to ? to : new Date(Date.now()); const totalRefundAmount = await this.pgConnection('refund') .join('payment', 'refund.payment_id', 'payment.id') .sum('refund.amount AS sum') .where('refund.created_at', '>=', startQueryFrom) .andWhere('refund.created_at', '<=', endQuery) .andWhere('payment.currency_code', '=', currencyCode) .first(); return { currencyCode: currencyCode, currencyDecimalDigits: (0, currency_1.getDecimalDigits)(currencyCode), dateRangeFrom: startQueryFrom.getTime(), dateRangeTo: to ? to.getTime() : new Date(Date.now()).getTime(), dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: totalRefundAmount !== undefined ? totalRefundAmount.sum : '0', previous: undefined }; } return { currencyCode: currencyCode, currencyDecimalDigits: (0, currency_1.getDecimalDigits)(currencyCode), dateRangeFrom: undefined, dateRangeTo: undefined, dateRangeFromCompareTo: undefined, dateRangeToCompareTo: undefined, current: undefined, previous: undefined }; } } exports.SalesAnalyticsService = SalesAnalyticsService; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FsZXNBbmFseXRpY3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9zdG9yZS1hbmFseXRpY3Mvc2VydmljZXMvc2FsZXNBbmFseXRpY3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7O0dBVUc7OztBQUdILHdFQUE2RztBQUU3RywrQ0FBbUQ7QUE2RG5ELFNBQVMsWUFBWSxDQUFDLE1BQWEsRUFBRSxVQUE4QixFQUFFLGlCQUF5QjtJQUM1RixNQUFNLGdCQUFnQixHQUFHLElBQUEseUNBQW1CLEVBQUMsVUFBVSxDQUFDLENBQUM7SUFDekQsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsV0FBVyxFQUFFLEtBQUssRUFBRSxFQUFFO1FBQzFDLE1BQU0sYUFBYSxHQUFHLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxVQUFrQixDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsV0FBVyxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQzlDLElBQUksVUFBVSxJQUFJLHdDQUFrQixDQUFDLEdBQUcsRUFBRSxDQUFDO2dCQUN6QyxXQUFXLENBQUMsYUFBYSxDQUFDLFdBQVcsRUFBRSxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUsSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxFQUFDLENBQUMsRUFBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN4SCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sV0FBVyxDQUFDLGFBQWEsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FB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