Initial commit: backend, storefront, vendor-panel added

This commit is contained in:
2025-08-01 11:05:32 +08:00
commit 08174125d2
2958 changed files with 310810 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
{
"name": "@mercurjs/commission",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"!dist/**/__tests__",
"!dist/**/__mocks__",
"!dist/**/__fixtures__"
],
"engines": {
"node": ">=20"
},
"license": "MIT",
"scripts": {
"build": "rimraf dist && tsc --build",
"migration:initial": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:create --initial",
"migration:create": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:create",
"migration:up": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:up",
"orm:cache:clear": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm cache:clear"
},
"devDependencies": {
"@medusajs/framework": "2.8.6",
"@medusajs/test-utils": "2.8.6",
"@mercurjs/framework": "*",
"@mikro-orm/cli": "6.4.3",
"@mikro-orm/core": "6.4.3",
"@mikro-orm/migrations": "6.4.3",
"@mikro-orm/postgresql": "6.4.3",
"@swc/core": "^1.7.28",
"@swc/jest": "^0.2.36",
"jest": "^29.7.0",
"rimraf": "^3.0.2",
"tsc-alias": "^1.8.6",
"typescript": "^5.6.2"
},
"peerDependencies": {
"@medusajs/framework": "2.8.6",
"@mikro-orm/core": "6.4.3",
"@mikro-orm/migrations": "6.4.3",
"@mikro-orm/postgresql": "6.4.3",
"awilix": "^8.0.1"
}
}

View File

@@ -0,0 +1,10 @@
import { Module } from "@medusajs/framework/utils";
import CommissionModuleService from "./service";
export const COMMISSION_MODULE = "commission";
export { CommissionModuleService };
export default Module(COMMISSION_MODULE, {
service: CommissionModuleService,
});

View File

@@ -0,0 +1,388 @@
{
"namespaces": [
"public"
],
"name": "public",
"tables": [
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"item_line_id": {
"name": "item_line_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"rule_id": {
"name": "rule_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"currency_code": {
"name": "currency_code",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"value": {
"name": "value",
"type": "numeric",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "decimal"
},
"raw_value": {
"name": "raw_value",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "json"
},
"created_at": {
"name": "created_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"updated_at": {
"name": "updated_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"deleted_at": {
"name": "deleted_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
}
},
"name": "commission_line",
"schema": "public",
"indexes": [
{
"keyName": "IDX_commission_line_deleted_at",
"columnNames": [],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_commission_line_deleted_at\" ON \"commission_line\" (deleted_at) WHERE deleted_at IS NULL"
},
{
"keyName": "commission_line_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"name": {
"name": "name",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"reference": {
"name": "reference",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"reference_id": {
"name": "reference_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"is_active": {
"name": "is_active",
"type": "boolean",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "true",
"mappedType": "boolean"
},
"created_at": {
"name": "created_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"updated_at": {
"name": "updated_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"deleted_at": {
"name": "deleted_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
}
},
"name": "commission_rule",
"schema": "public",
"indexes": [
{
"keyName": "IDX_commission_rule_deleted_at",
"columnNames": [],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_commission_rule_deleted_at\" ON \"commission_rule\" (deleted_at) WHERE deleted_at IS NULL"
},
{
"keyName": "commission_rule_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"type": {
"name": "type",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"percentage_rate": {
"name": "percentage_rate",
"type": "integer",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "integer"
},
"include_tax": {
"name": "include_tax",
"type": "boolean",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "boolean"
},
"price_set_id": {
"name": "price_set_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"max_price_set_id": {
"name": "max_price_set_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"min_price_set_id": {
"name": "min_price_set_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"rule_id": {
"name": "rule_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"created_at": {
"name": "created_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"updated_at": {
"name": "updated_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 6,
"default": "now()",
"mappedType": "datetime"
},
"deleted_at": {
"name": "deleted_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
}
},
"name": "commission_rate",
"schema": "public",
"indexes": [
{
"columnNames": [
"rule_id"
],
"composite": false,
"keyName": "commission_rate_rule_id_unique",
"primary": false,
"unique": true
},
{
"keyName": "IDX_commission_rate_rule_id",
"columnNames": [],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_commission_rate_rule_id\" ON \"commission_rate\" (rule_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_commission_rate_deleted_at",
"columnNames": [],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_commission_rate_deleted_at\" ON \"commission_rate\" (deleted_at) WHERE deleted_at IS NULL"
},
{
"keyName": "commission_rate_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"commission_rate_rule_id_foreign": {
"constraintName": "commission_rate_rule_id_foreign",
"columnNames": [
"rule_id"
],
"localTableName": "public.commission_rate",
"referencedColumnNames": [
"id"
],
"referencedTableName": "public.commission_rule",
"deleteRule": "set null",
"updateRule": "cascade"
}
}
}
]
}

View File

@@ -0,0 +1,30 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20250124152358 extends Migration {
async up(): Promise<void> {
this.addSql('create table if not exists "commission_line" ("id" text not null, "item_line_id" text not null, "rule_id" text not null, "currency_code" text not null, "value" numeric not null, "raw_value" jsonb not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "commission_line_pkey" primary key ("id"));');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_commission_line_deleted_at" ON "commission_line" (deleted_at) WHERE deleted_at IS NULL;');
this.addSql('create table if not exists "commission_rule" ("id" text not null, "name" text not null, "reference" text not null, "reference_id" text not null, "is_active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "commission_rule_pkey" primary key ("id"));');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_commission_rule_deleted_at" ON "commission_rule" (deleted_at) WHERE deleted_at IS NULL;');
this.addSql('create table if not exists "commission_rate" ("id" text not null, "type" text not null, "percentage_rate" integer not null, "include_tax" boolean not null, "price_set_id" text not null, "max_price_set_id" text not null, "min_price_set_id" text not null, "rule_id" text null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "commission_rate_pkey" primary key ("id"));');
this.addSql('alter table if exists "commission_rate" add constraint "commission_rate_rule_id_unique" unique ("rule_id");');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_commission_rate_rule_id" ON "commission_rate" (rule_id) WHERE deleted_at IS NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_commission_rate_deleted_at" ON "commission_rate" (deleted_at) WHERE deleted_at IS NULL;');
this.addSql('alter table if exists "commission_rate" add constraint "commission_rate_rule_id_foreign" foreign key ("rule_id") references "commission_rule" ("id") on update cascade on delete set null;');
}
async down(): Promise<void> {
this.addSql('alter table if exists "commission_rate" drop constraint if exists "commission_rate_rule_id_foreign";');
this.addSql('drop table if exists "commission_line" cascade;');
this.addSql('drop table if exists "commission_rule" cascade;');
this.addSql('drop table if exists "commission_rate" cascade;');
}
}

View File

@@ -0,0 +1,57 @@
import { Migration } from '@mikro-orm/migrations'
export class Migration20250127073504 extends Migration {
async up(): Promise<void> {
this.addSql(
'alter table if exists "commission_rate" alter column "percentage_rate" type integer using ("percentage_rate"::integer);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "percentage_rate" drop not null;'
)
this.addSql(
'alter table if exists "commission_rate" alter column "price_set_id" type text using ("price_set_id"::text);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "price_set_id" drop not null;'
)
this.addSql(
'alter table if exists "commission_rate" alter column "max_price_set_id" type text using ("max_price_set_id"::text);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "max_price_set_id" drop not null;'
)
this.addSql(
'alter table if exists "commission_rate" alter column "min_price_set_id" type text using ("min_price_set_id"::text);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "min_price_set_id" drop not null;'
)
}
async down(): Promise<void> {
this.addSql(
'alter table if exists "commission_rate" alter column "percentage_rate" type integer using ("percentage_rate"::integer);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "percentage_rate" set not null;'
)
this.addSql(
'alter table if exists "commission_rate" alter column "price_set_id" type text using ("price_set_id"::text);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "price_set_id" set not null;'
)
this.addSql(
'alter table if exists "commission_rate" alter column "max_price_set_id" type text using ("max_price_set_id"::text);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "max_price_set_id" set not null;'
)
this.addSql(
'alter table if exists "commission_rate" alter column "min_price_set_id" type text using ("min_price_set_id"::text);'
)
this.addSql(
'alter table if exists "commission_rate" alter column "min_price_set_id" set not null;'
)
}
}

View File

@@ -0,0 +1,9 @@
import { model } from '@medusajs/framework/utils'
export const CommissionLine = model.define('commission_line', {
id: model.id({ prefix: 'com_line' }).primaryKey(),
item_line_id: model.text(),
rule_id: model.text(),
currency_code: model.text(),
value: model.bigNumber()
})

View File

@@ -0,0 +1,18 @@
import { model } from '@medusajs/framework/utils'
import { CommissionRule } from './commission_rule'
export const CommissionRate = model.define('commission_rate', {
id: model.id({ prefix: 'com_rate' }).primaryKey(),
type: model.text(),
percentage_rate: model.number().nullable(),
include_tax: model.boolean(),
price_set_id: model.text().nullable(),
max_price_set_id: model.text().nullable(),
min_price_set_id: model.text().nullable(),
rule: model
.belongsTo(() => CommissionRule, {
mappedBy: 'rate'
})
.nullable()
})

View File

@@ -0,0 +1,14 @@
import { model } from '@medusajs/framework/utils'
import { CommissionRate } from './commission_rate'
export const CommissionRule = model.define('commission_rule', {
id: model.id({ prefix: 'com_rule' }).primaryKey(),
name: model.text().searchable(),
reference: model.text().searchable(),
reference_id: model.text(),
is_active: model.boolean().default(true),
rate: model.hasOne(() => CommissionRate, {
mappedBy: 'rule'
})
})

View File

@@ -0,0 +1,3 @@
export * from './commission_rate'
export * from './commission_rule'
export * from './commission_line'

View File

@@ -0,0 +1,58 @@
import { MedusaService } from "@medusajs/framework/utils";
import { CommissionRate, CommissionRule } from "./models";
import { CommissionLine } from "./models/commission_line";
import {
CommissionCalculationContext,
CommissionRuleDTO,
} from "@mercurjs/framework";
class CommissionModuleService extends MedusaService({
CommissionRate,
CommissionRule,
CommissionLine,
}) {
private async selectCommissionRule(reference: string, reference_id: string) {
const [rule] = await this.listCommissionRules(
{ reference, reference_id, is_active: true, deleted_at: null },
{ relations: ["rate"] }
);
return rule;
}
/**
* Looks for first applicable CommissionRule for given context. The queries are executed in assumed priority order.
* @param ctx Calculation context
* @returns CommissionRule applicable for given context or null
*/
async selectCommissionForProductLine(
ctx: CommissionCalculationContext
): Promise<CommissionRuleDTO | null> {
const ruleQueries = [
{
reference: "seller+product_type",
reference_id: `${ctx.seller_id}+${ctx.product_type_id}`,
},
{
reference: "seller+product_category",
reference_id: `${ctx.seller_id}+${ctx.product_category_id}`,
},
{ reference: "seller", reference_id: ctx.seller_id },
{ reference: "product_type", reference_id: ctx.product_type_id },
{ reference: "product_category", reference_id: ctx.product_category_id },
{ reference: "site", reference_id: "" },
];
for (const { reference, reference_id } of ruleQueries) {
const rule = await this.selectCommissionRule(reference, reference_id);
if (rule) {
return rule;
}
}
return null;
}
}
export default CommissionModuleService;

View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"lib": ["ES2021"],
"target": "ES2021",
"outDir": "${configDir}/dist",
"esModuleInterop": true,
"declaration": true,
"declarationMap": true,
"noUnusedLocals": true,
"module": "node16",
"moduleResolution": "node16",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"noImplicitReturns": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"allowJs": true,
"skipLibCheck": true,
"incremental": false
},
"include": ["${configDir}/src"],
"exclude": ["${configDir}/dist", "${configDir}/node_modules"]
}