@rsc-labs/medusa-store-analytics-v2
Version:
Get analytics data about your store
436 lines • 41.6 kB
JavaScript
;
/*
* 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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