-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Description
Package.json file
{
"name": "backend",
"version": "0.0.1",
"description": "A starter for Medusa projects.",
"author": "Medusa (https://medusajs.com)",
"license": "MIT",
"keywords": [
"sqlite",
"postgres",
"typescript",
"ecommerce",
"headless",
"medusa"
],
"scripts": {
"build": "medusa build",
"seed": "medusa exec ./src/scripts/seed.ts",
"start": "medusa start",
"dev": "medusa develop --host 127.0.0.1 --port 8101",
"format": "biome format --write --verbose .",
"migrate": "KNEX_POOL_MAX=2 KNEX_ACQUIRE_TIMEOUT=60000 medusa db:migrate --execute-all-links",
"test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
"test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
"test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
},
"dependencies": {
"@medusajs/admin-sdk": "2.3.1",
"@medusajs/cli": "2.3.1",
"@medusajs/framework": "2.3.1",
"@medusajs/medusa": "2.3.1",
"@mikro-orm/core": "5.9.7",
"@mikro-orm/knex": "5.9.7",
"@mikro-orm/migrations": "5.9.7",
"@mikro-orm/postgresql": "5.9.7",
"@retail/types": "*",
"awilix": "^8.0.1",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"luxon": "^3.2.1",
"nanoid": "^3.3.8",
"pg": "^8.13.0",
"scrypt-kdf": "^2.0.1",
"zod": "3.22.4"
},
"devDependencies": {
"@medusajs/test-utils": "2.3.1",
"@mikro-orm/cli": "5.9.7",
"@swc/core": "1.5.7",
"@swc/jest": "^0.2.36",
"@types/jest": "^29.5.13",
"@types/jsonwebtoken": "^9.0.2",
"@types/lodash": "^4.17.15",
"@types/luxon": "^3.2.1",
"@types/node": "^20.0.0",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.2.25",
"jest": "^29.7.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ts-node": "^10.9.2",
"typescript": "^5.6.2",
"vite": "^5.2.11"
},
"engines": {
"node": ">=20"
},
"packageManager": "[email protected]"
}
Node.js version
v22.11.0
Database and its version
PostgreSQL 15
Operating system name and version
MacOS 15.7.1
Browser name
No response
What happended?
When creating an order line item with a decimal quantity, Medusa calculates intermediate monetary values without rounding to the currency’s minor unit (2 decimals for USD).
Why using decimal quantities?
In our client’s retail operations, they sell goods by length or volume (for instance, fabrics, linens, or trim) where customers often request fractional quantities like 0.5 or 1.25 units (e.g. half a yard). Because of this, the system must support decimal quantities (not just integers) and compute correct cent-level amounts after multiplying by unit price and applying tax.
Example case:
quantity = 0.5
unit_price = 1.99
tax = 7% (is_tax_inclusive = false)
Medusa internally computes:
subtotal = 0.5 * 1.99 = 0.995
tax_total = 0.995 * 0.07 = 0.06965
total = 0.995 + 0.06965 = 1.06465
Because rounding occurs only at the final total conversion (getSmallestUnit), the value becomes $1.06 instead of the expected $1.07.
This discrepancy causes invoices and payments to mismatch by one cent.
The problematic behavior originates in createOrderLineItemsStep → getLineItemTotals (framework) → calculateTaxTotal.
These functions use BigNumber math but never round after quantity * unit_price or after tax calculations.
Possible Affected areas:
packages/core/core-flows/src/order/steps/create-order-line-items.ts
packages/core/core-flows/src/cart/utils/totals.ts
packages/core/core-flows/src/totals/calculate-tax-total.ts
@medusajs/framework BigNumber transformations and final DTO rounding (decorateCartTotals)
Expected behavior
Each order line item should normalize monetary amounts at the appropriate precision step:
Line Subtotal: round(quantity × unit_price) → $1.00
Line Tax: round(subtotal × tax_rate) → $0.07
Line Total: subtotal + tax_total → $1.07
Totals at the order level should equal the sum of individually rounded lines, matching invoices and payment processor totals.
Actual behavior
Medusa retains high-precision BigNumbers through the pipeline and only rounds at the final payment conversion (getSmallestUnit):
subtotal: 0.995
tax_total: 0.06965
total: 1.06465 → rounds to $1.06
JSON Payload Response for POST {{baseUrl}}/admin/orders/{{orderId}}/line-items
{
"order": {
"id": "order_01K77FV6T57PNHNSMMQ91K96XQ",
"display_id": 1436,
"status": "draft",
"version": 1,
"summary": {
"paid_total": 0,
"difference_sum": 0,
"raw_paid_total": {
"value": "0",
"precision": 20
},
"refunded_total": 0,
"accounting_total": 0,
"credit_line_total": 0,
"transaction_total": 0,
"pending_difference": 0,
"raw_difference_sum": {
"value": "0",
"precision": 20
},
"raw_refunded_total": {
"value": "0",
"precision": 20
},
"current_order_total": 0,
"original_order_total": 0,
"raw_accounting_total": {
"value": "0",
"precision": 20
},
"raw_credit_line_total": {
"value": "0",
"precision": 20
},
"raw_transaction_total": {
"value": "0",
"precision": 20
},
"raw_pending_difference": {
"value": "0",
"precision": 20
},
"raw_current_order_total": {
"value": "0",
"precision": 20
},
"raw_original_order_total": {
"value": "0",
"precision": 20
}
},
"metadata": null,
"created_at": "2025-10-10T16:39:10.917Z",
"updated_at": "2025-10-10T16:39:10.917Z",
"region_id": "reg_01JH897JQ0K4EH3AKN6CZ8JXPZ",
"customer_id": "cus_01JM5QCAKJA3DNHFPZ40P4NSVP",
"total": 1.06465,
"subtotal": 0.995,
"tax_total": 0.06965,
"discount_total": 0,
"discount_tax_total": 0,
"original_total": 1.06465,
"original_tax_total": 0.06965,
"item_total": 1.06465,
"item_subtotal": 0.995,
"item_tax_total": 0.06965,
"original_item_total": 1.06465,
"original_item_subtotal": 0.995,
"original_item_tax_total": 0.06965,
"shipping_total": 0,
"shipping_subtotal": 0,
"shipping_tax_total": 0,
"original_shipping_tax_total": 0,
"original_shipping_subtotal": 0,
"original_shipping_total": 0,
"items": [
{
"id": "ordli_01K77GRXEAY058ZGP8Q2049HX3",
"title": "Telas/Sedería",
"subtitle": "Telas/Sedería",
"thumbnail": null,
"variant_id": "variant_01JJVRN3PQJ7Q8GYDP0SZ7VAP5",
"product_id": "prod_01JJVRN3MVZW2NQF9CT5CJ15AT",
"product_title": "Telas/Sedería",
"product_description": null,
"product_subtitle": null,
"product_type": null,
"product_type_id": null,
"product_collection": null,
"product_handle": "telassederia",
"variant_sku": "57",
"variant_barcode": null,
"variant_title": "Telas/Sedería",
"variant_option_values": null,
"requires_shipping": false,
"is_discountable": true,
"is_tax_inclusive": false,
"raw_compare_at_unit_price": null,
"raw_unit_price": {
"value": "1.99",
"precision": 20
},
"is_custom_price": true,
"metadata": {},
"created_at": "2025-10-10T16:55:24.363Z",
"updated_at": "2025-10-10T16:55:24.363Z",
"deleted_at": null,
"tax_lines": [
{
"id": "ordlitxl_01K77GRXEAGN7SD7DD6ABX21WA",
"description": "ITBMS 7%",
"tax_rate_id": "txr_01JHTY5QSKN8RVTZ2RQJ4KDRMK",
"code": "pa_tax_7",
"raw_rate": {
"value": "7",
"precision": 20
},
"provider_id": "system",
"created_at": "2025-10-10T16:55:24.363Z",
"updated_at": "2025-10-10T16:55:24.363Z",
"deleted_at": null,
"item_id": "ordli_01K77GRXEAY058ZGP8Q2049HX3",
"rate": 7,
"total": 0.06965,
"subtotal": 0.06965,
"raw_total": {
"value": "0.06965",
"precision": 20
},
"raw_subtotal": {
"value": "0.06965",
"precision": 20
}
}
],
"adjustments": [],
"compare_at_unit_price": null,
"unit_price": 1.99,
"quantity": 0.5,
"raw_quantity": {
"value": "0.5",
"precision": 20
},
"detail": {
"id": "orditem_01K77GRXEAS4VX2PFXCY2G2W0T",
"order_id": "order_01K77FV6T57PNHNSMMQ91K96XQ",
"version": 1,
"item_id": "ordli_01K77GRXEAY058ZGP8Q2049HX3",
"raw_unit_price": null,
"raw_compare_at_unit_price": null,
"raw_quantity": {
"value": "0.5",
"precision": 20
},
"raw_fulfilled_quantity": {
"value": "0",
"precision": 20
},
"raw_delivered_quantity": {
"value": "0",
"precision": 20
},
"raw_shipped_quantity": {
"value": "0",
"precision": 20
},
"raw_return_requested_quantity": {
"value": "0",
"precision": 20
},
"raw_return_received_quantity": {
"value": "0",
"precision": 20
},
"raw_return_dismissed_quantity": {
"value": "0",
"precision": 20
},
"raw_written_off_quantity": {
"value": "0",
"precision": 20
},
"metadata": null,
"created_at": "2025-10-10T16:55:24.363Z",
"updated_at": "2025-10-10T16:55:24.363Z",
"deleted_at": null,
"unit_price": null,
"compare_at_unit_price": null,
"quantity": 0.5,
"fulfilled_quantity": 0,
"delivered_quantity": 0,
"shipped_quantity": 0,
"return_requested_quantity": 0,
"return_received_quantity": 0,
"return_dismissed_quantity": 0,
"written_off_quantity": 0
},
"subtotal": 0.995,
"total": 1.06465,
"original_total": 1.06465,
"discount_total": 0,
"discount_subtotal": 0,
"discount_tax_total": 0,
"tax_total": 0.06965,
"original_tax_total": 0.06965,
"refundable_total_per_unit": 2.1293,
"refundable_total": 1.06465,
"fulfilled_total": 0,
"shipped_total": 0,
"return_requested_total": 0,
"return_received_total": 0,
"return_dismissed_total": 0,
"write_off_total": 0,
"raw_subtotal": {
"value": "0.995",
"precision": 20
},
"raw_total": {
"value": "1.06465",
"precision": 20
},
"raw_original_total": {
"value": "1.06465",
"precision": 20
},
"raw_discount_total": {
"value": "0",
"precision": 20
},
"raw_discount_subtotal": {
"value": "0",
"precision": 20
},
"raw_discount_tax_total": {
"value": "0",
"precision": 20
},
"raw_tax_total": {
"value": "0.06965",
"precision": 20
},
"raw_original_tax_total": {
"value": "0.06965",
"precision": 20
},
"raw_refundable_total_per_unit": {
"value": "2.1293",
"precision": 20
},
"raw_refundable_total": {
"value": "1.06465",
"precision": 20
},
"raw_fulfilled_total": {
"value": "0",
"precision": 20
},
"raw_shipped_total": {
"value": "0",
"precision": 20
},
"raw_return_requested_total": {
"value": "0",
"precision": 20
},
"raw_return_received_total": {
"value": "0",
"precision": 20
},
"raw_return_dismissed_total": {
"value": "0",
"precision": 20
},
"raw_write_off_total": {
"value": "0",
"precision": 20
},
"variant": {
"id": "variant_01JJVRN3PQJ7Q8GYDP0SZ7VAP5",
"title": "Telas/Sedería",
"sku": "57",
"barcode": null,
"ean": null,
"upc": null,
"allow_backorder": false,
"manage_inventory": false,
"hs_code": null,
"origin_country": null,
"mid_code": null,
"material": null,
"weight": null,
"length": null,
"height": null,
"width": null,
"metadata": null,
"variant_rank": 0,
"product_id": "prod_01JJVRN3MVZW2NQF9CT5CJ15AT",
"product": {
"id": "prod_01JJVRN3MVZW2NQF9CT5CJ15AT",
"title": "Telas/Sedería",
"handle": "telassederia",
"subtitle": null,
"description": null,
"is_giftcard": false,
"status": "published",
"thumbnail": null,
"weight": null,
"length": null,
"height": null,
"width": null,
"origin_country": null,
"hs_code": null,
"mid_code": null,
"material": null,
"discountable": true,
"external_id": null,
"metadata": null,
"type_id": null,
"type": null,
"collection_id": null,
"collection": null,
"created_at": "2025-01-30T14:08:18.577Z",
"updated_at": "2025-01-30T14:08:18.577Z",
"deleted_at": null
},
"created_at": "2025-01-30T14:08:18.648Z",
"updated_at": "2025-01-30T14:08:18.648Z",
"deleted_at": null
}
}
],
"shipping_address": {
"id": "ordaddr_01K77FV6T5T3JSM4GNE7HQQ8SH",
"customer_id": null,
"company": "Disclosed",
"first_name": null,
"last_name": null,
"address_1": null,
"address_2": null,
"city": null,
"country_code": "pa",
"province": null,
"postal_code": null,
"phone": null,
"metadata": null,
"created_at": "2025-10-10T16:39:10.917Z",
"updated_at": "2025-10-10T16:39:10.917Z"
},
"billing_address": null,
"shipping_methods": [],
"customer": {
"id": "cus_01JM5QCAKJA3DNHFPZ40P4NSVP",
"company_name": null,
"first_name": "CF",
"last_name": null,
"email": null,
"phone": null,
"has_account": false,
"metadata": {
"default": true
},
"created_by": null,
"created_at": "2025-02-15T21:14:39.440Z",
"updated_at": "2025-02-15T21:14:39.440Z",
"deleted_at": null
},
"payment_collections": [],
"pos_session": {
"id": "posses_01K77AD24ANXJFTK16DV5Q88AY",
"public_id": "E1-ZYZ15QQ",
"status": "active",
"summary": {
"initial_amount": 100
},
"totals": null,
"notes": null,
"closed_at": null,
"reviewed_at": null,
"last_payment_at": null,
"pos_id": "pos_01JKGCWY6YY6Z8HNSK1TT1E8K0",
"pos": {
"id": "pos_01JKGCWY6YY6Z8HNSK1TT1E8K0"
},
"created_at": "2025-10-10T15:04:04.490Z",
"updated_at": "2025-10-10T15:04:04.490Z",
"deleted_at": null
},
"fulfillments": [],
"payment_status": "not_paid",
"fulfillment_status": "not_fulfilled"
}
}