karrio-server-pricing 2026.1.3__py3-none-any.whl → 2026.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -193,14 +193,22 @@ class Markup(core.Entity):
193
193
  else (rate.total_charge * (typing.cast(float, self.amount) / 100))
194
194
  )
195
195
  total_charge = lib.to_decimal(rate.total_charge + amount)
196
- extra_charges = rate.extra_charges + [
197
- karrio.ChargeDetails(
198
- name=typing.cast(str, self.name),
199
- amount=amount,
200
- currency=rate.currency,
201
- id=self.id,
202
- )
203
- ]
196
+
197
+ # Only add to extra_charges if the markup is visible.
198
+ # Non-visible markups (e.g., plan-based platform fees) are
199
+ # included in total_charge but hidden from the breakdown.
200
+ extra_charges = (
201
+ rate.extra_charges + [
202
+ karrio.ChargeDetails(
203
+ name=typing.cast(str, self.name),
204
+ amount=amount,
205
+ currency=rate.currency,
206
+ id=self.id,
207
+ )
208
+ ]
209
+ if self.is_visible
210
+ else rate.extra_charges
211
+ )
204
212
 
205
213
  return datatypes.Rate(
206
214
  **{
@@ -34,11 +34,17 @@ def apply_custom_markups(context: Context, result):
34
34
  Apply active markups to rate quotes.
35
35
 
36
36
  This function is called after rates are fetched from carriers.
37
- It applies all active markups that match the organization context.
37
+ It applies all active markups that match the organization context
38
+ and the tenant's pricing plan.
38
39
 
39
40
  Markup scoping via organization_ids JSONField:
40
41
  - Markups with org ID in organization_ids apply only to that org
41
42
  - Markups with empty organization_ids are system-wide
43
+
44
+ Plan filtering via metadata.plan:
45
+ - Markups with no plan in metadata are global (apply to all plans)
46
+ - Markups with metadata.plan only apply when the tenant's plan matches
47
+ - Plan is resolved from: request filter > org metadata > default "launch"
42
48
  """
43
49
  org_id = getattr(context.org, "id", None)
44
50
 
@@ -46,8 +52,10 @@ def apply_custom_markups(context: Context, result):
46
52
  # Filter markups that either:
47
53
  # 1. Have the current organization in their organization_ids list
48
54
  # 2. Have an empty organization_ids list (system-wide markups)
55
+ # Note: icontains is used instead of __contains for cross-DB
56
+ # compatibility (SQLite does not support JSON containment lookups).
49
57
  _filters = (
50
- Q(active=True, organization_ids__contains=[org_id])
58
+ Q(active=True, organization_ids__icontains=org_id)
51
59
  | Q(active=True, organization_ids=[]),
52
60
  )
53
61
  else:
@@ -56,9 +64,38 @@ def apply_custom_markups(context: Context, result):
56
64
 
57
65
  markups = models.Markup.objects.filter(*_filters)
58
66
 
67
+ # Resolve the tenant's plan for plan-based markup filtering.
68
+ # Priority: explicit request filter > org metadata > default "launch"
69
+ request_plan = None
70
+ context_data = None
71
+ if hasattr(context, "_full_data"):
72
+ context_data = context._full_data
73
+ elif hasattr(context, "data"):
74
+ context_data = context.data
75
+
76
+ if context_data:
77
+ filters = context_data.get("filters") or {}
78
+ request_plan = filters.get("plan")
79
+
80
+ if request_plan:
81
+ tenant_plan = request_plan
82
+ else:
83
+ org_metadata = getattr(
84
+ getattr(context, "org", None), "metadata", {}
85
+ ) or {}
86
+ tenant_plan = org_metadata.get("plan", "launch")
87
+
88
+ # Filter markups by plan:
89
+ # Apply if markup has no plan in metadata (global) OR plan matches tenant's plan
90
+ def matches_plan(markup):
91
+ markup_plan = (markup.metadata or {}).get("plan")
92
+ return markup_plan is None or markup_plan == tenant_plan
93
+
94
+ applicable_markups = [m for m in markups if matches_plan(m)]
95
+
59
96
  return functools.reduce(
60
97
  lambda cumulated_result, markup: markup.apply_charge(cumulated_result),
61
- markups,
98
+ applicable_markups,
62
99
  result,
63
100
  )
64
101
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karrio_server_pricing
3
- Version: 2026.1.3
3
+ Version: 2026.1.5
4
4
  Summary: Multi-carrier shipping API Pricing panel
5
5
  Author-email: karrio <hello@karrio.io>
6
6
  License-Expression: LGPL-3.0
@@ -1,9 +1,9 @@
1
1
  karrio/server/pricing/__init__.py,sha256=zXpxAFuHsCdAaESYGLRvdqCs7CXtIn8cF688jV4ufQc,64
2
2
  karrio/server/pricing/admin.py,sha256=vDYvoFk9M6bGK6NBoJCRkxL5dla4sibNfej69_RIOT8,5642
3
3
  karrio/server/pricing/apps.py,sha256=LBpxtFvuUn36DjMiPlQL51V5WfVMDf7N4QS3IUs_Pcs,431
4
- karrio/server/pricing/models.py,sha256=gRcCHNEQpQSP4T44RvyPInNYzXL3SHXq9Ar0f9il3YY,11697
4
+ karrio/server/pricing/models.py,sha256=AfgSmMcN7H22_qLRlK5InWmdfnZi_YHMtQD-LJJhMr0,12040
5
5
  karrio/server/pricing/serializers.py,sha256=48panQv9ZyLzkVa94XzOqbXxFm0daMK1_jOVJwQiNbI,574
6
- karrio/server/pricing/signals.py,sha256=-XNTjbnn1AnRZDP45YXIaSTXdYG6tXrnBXnCX9CFDOo,6560
6
+ karrio/server/pricing/signals.py,sha256=-Q-SN0wElBhBJRfBF5Iez7Qkxte1fQ0eLpnOOJzPBkw,8034
7
7
  karrio/server/pricing/tests.py,sha256=iaTqsY2pSPR1YRy9TqjhkfvRN8776l0Zoh4xeT0awQc,24818
8
8
  karrio/server/pricing/views.py,sha256=xc1IQHrsij7j33TUbo-_oewy3vs03pw_etpBWaMYJl0,63
9
9
  karrio/server/pricing/migrations/0001_initial.py,sha256=KKEklxf8Q1-IQn-8ZchbCi3-zmIyjn80DaGqPgRRI5w,52327
@@ -86,7 +86,7 @@ karrio/server/pricing/migrations/0077_migrate_surcharge_to_markup_data.py,sha256
86
86
  karrio/server/pricing/migrations/0078_cleanup.py,sha256=aEO5ceQ6-35jz3mGblQ2RD_144Uu-gVAjaEckqHM1OM,4263
87
87
  karrio/server/pricing/migrations/0079_fee_snapshot_model.py,sha256=n18S32UPAp_WMzVL4_Hf96drwGPE72pc45Ul2qPs-nk,9843
88
88
  karrio/server/pricing/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- karrio_server_pricing-2026.1.3.dist-info/METADATA,sha256=MJAX7agk7MRZSlPuQ7g0zOUHWr70ymy9D5w5oXDQ5-I,684
90
- karrio_server_pricing-2026.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
91
- karrio_server_pricing-2026.1.3.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
92
- karrio_server_pricing-2026.1.3.dist-info/RECORD,,
89
+ karrio_server_pricing-2026.1.5.dist-info/METADATA,sha256=0TayHwWKp9oIXYgD6rqDflmIK5Qdu9BT-KDN49g04VM,684
90
+ karrio_server_pricing-2026.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
91
+ karrio_server_pricing-2026.1.5.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
92
+ karrio_server_pricing-2026.1.5.dist-info/RECORD,,