* feat: Add DiscountCondition entity + Join table per relation (#1146) * feat: Convert DiscountService to TypeScript (#1149) * feat: Add DiscountRepository + bulk insert and remove (#1156) * feat: Add `conditions` to payload in `POST /discounts` and `POST /discounts/:id` (#1170) * feat: Add DiscountRuleCondition entity * fix relation * fix join key * Add discount rule condition repo * add join table per relation * Convert DiscountService to TypeScript * feat: Add DiscountConditionRepository * Add migration + remove use of valid_for * revert changes to files, not done yet * init work on create discount endpoint * Add conditions to create discount endpoint * Add conditions to update discount endpoint * Add unique constraint to discount condition * integration tests passing * fix imports of models * fix tests (excluding totals calculations) * Fix commented code * add unique constraint on discount condition * Add generic way of generating retrieve configs * Requested changes + ExactlyOne validator * Remove isLocal flag from error handler * Use postgres error constant * remove commented code * feat: Add `isValidForProduct` to check if Discount is valid for a given Product (#1172) * feat: Add `canApplyForCustomer` to check if Discount is valid for customer groups (#1212) * feat: Add `calculateDiscountForLineItem` (#1224) * feat: Adds discount condition test factory (#1228) * Remove use of valid_for * Tests passing * Remove valid_for form relations * Add integration tests for applying discounts to cart
187 lines
4.7 KiB
JavaScript
187 lines
4.7 KiB
JavaScript
import axios from "axios"
|
|
import { humanizeAmount, zeroDecimalCurrencies } from "medusa-core-utils"
|
|
import { BaseService } from "medusa-interfaces"
|
|
|
|
class SlackService extends BaseService {
|
|
/**
|
|
* @param {Object} options - options defined in `medusa-config.js`
|
|
* {
|
|
* show_discount_code: If set to true the discount code used will be
|
|
* displayed in the order channel.
|
|
* slack_url: "https://hooks.slack.com/services/...",
|
|
* admin_orders_url: "https:..../orders"
|
|
* }
|
|
*/
|
|
constructor({ orderService, totalsService, regionService }, options) {
|
|
super()
|
|
|
|
this.orderService_ = orderService
|
|
|
|
this.totalsService_ = totalsService
|
|
|
|
this.regionService_ = regionService
|
|
|
|
this.options_ = options
|
|
}
|
|
|
|
async orderNotification(orderId) {
|
|
const order = await this.orderService_.retrieve(orderId, {
|
|
select: [
|
|
"shipping_total",
|
|
"discount_total",
|
|
"tax_total",
|
|
"refunded_total",
|
|
"gift_card_total",
|
|
"subtotal",
|
|
"total",
|
|
],
|
|
relations: [
|
|
"customer",
|
|
"billing_address",
|
|
"shipping_address",
|
|
"discounts",
|
|
"discounts.rule",
|
|
"shipping_methods",
|
|
"payments",
|
|
"fulfillments",
|
|
"returns",
|
|
"gift_cards",
|
|
"gift_card_transactions",
|
|
"swaps",
|
|
"swaps.return_order",
|
|
"swaps.payment",
|
|
"swaps.shipping_methods",
|
|
"swaps.shipping_address",
|
|
"swaps.additional_items",
|
|
"swaps.fulfillments",
|
|
],
|
|
})
|
|
|
|
const { subtotal, tax_total, discount_total, shipping_total, total } = order
|
|
|
|
const currencyCode = order.currency_code.toUpperCase()
|
|
const getDisplayAmount = (amount) => {
|
|
const humanAmount = humanizeAmount(amount, currencyCode)
|
|
if (zeroDecimalCurrencies.includes(currencyCode.toLowerCase())) {
|
|
return humanAmount
|
|
}
|
|
return humanAmount.toFixed(2)
|
|
}
|
|
|
|
const blocks = [
|
|
{
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: `Order *<${this.options_.admin_orders_url}/${order.id}|#${order.display_id}>* has been processed.`,
|
|
},
|
|
},
|
|
{
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: `*Customer*\n${order.shipping_address.first_name} ${
|
|
order.shipping_address.last_name
|
|
}\n${order.email}\n*Destination*\n${
|
|
order.shipping_address.address_1
|
|
}\n${
|
|
order.shipping_address.city
|
|
}, ${order.shipping_address.country_code.toUpperCase()}`,
|
|
},
|
|
},
|
|
{
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: `*Subtotal*\t${getDisplayAmount(
|
|
subtotal
|
|
)} ${currencyCode}\n*Shipping*\t${getDisplayAmount(
|
|
shipping_total
|
|
)} ${currencyCode}\n*Discount Total*\t${getDisplayAmount(
|
|
discount_total
|
|
)} ${currencyCode}\n*Tax*\t${getDisplayAmount(
|
|
tax_total
|
|
)} ${currencyCode}\n*Total*\t${getDisplayAmount(
|
|
total
|
|
)} ${currencyCode}`,
|
|
},
|
|
},
|
|
]
|
|
|
|
if (order.gift_card_total) {
|
|
blocks.push({
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: `*Gift Card Total*\t${getDisplayAmount(
|
|
order.gift_card_total
|
|
)} ${currencyCode}`,
|
|
},
|
|
})
|
|
}
|
|
|
|
if (this.options_.show_discount_code) {
|
|
order.discounts.forEach((d) => {
|
|
blocks.push({
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: `*Promo Code*\t${d.code} ${d.rule.value}${
|
|
d.rule.type === "percentage" ? "%" : ` ${currencyCode}`
|
|
}`,
|
|
},
|
|
})
|
|
})
|
|
}
|
|
|
|
blocks.push({
|
|
type: "divider",
|
|
})
|
|
|
|
for (const lineItem of order.items) {
|
|
const totals = await this.totalsService_.getLineItemTotals(
|
|
lineItem,
|
|
order,
|
|
{
|
|
include_tax: true,
|
|
}
|
|
)
|
|
const line = {
|
|
type: "section",
|
|
text: {
|
|
type: "mrkdwn",
|
|
text: `*${lineItem.title}*\n${lineItem.quantity} x ${getDisplayAmount(
|
|
totals.original_total
|
|
)} ${currencyCode}`,
|
|
},
|
|
}
|
|
|
|
if (lineItem.thumbnail) {
|
|
let url = lineItem.thumbnail
|
|
if (lineItem.thumbnail.startsWith("//")) {
|
|
url = `https:${lineItem.thumbnail}`
|
|
}
|
|
|
|
line.accessory = {
|
|
type: "image",
|
|
alt_text: "Item",
|
|
image_url: url,
|
|
}
|
|
}
|
|
|
|
blocks.push(line)
|
|
|
|
blocks.push({
|
|
type: "divider",
|
|
})
|
|
}
|
|
|
|
return axios.post(this.options_.slack_url, {
|
|
text: `Order ${order.display_id} was processed`,
|
|
blocks,
|
|
})
|
|
}
|
|
}
|
|
|
|
export default SlackService
|