fix(fulfillment): Geozone constraints builder (#13282)
This commit is contained in:
1 parent
486621383a
commit
f0ef444992
2 files changed
+867
-21
No files matched your search
+829
@@ -1324,6 +1324,835 @@ moduleIntegrationTestRunner<IFulfillmentModuleService>({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("buildGeoZoneConstraintsFromAddress", () => {
|
||||||
|
it("should build correct constraints for full address with postal expression", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "San Francisco",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "CA",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test with full address including postal expression
|
||||||
|
const shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should build correct constraints for address with city but no postal expression", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "San Francisco",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test with city but no postal expression
|
||||||
|
const shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "San Francisco",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should build correct constraints for address with province but no city", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test with province but no city
|
||||||
|
const shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should build correct constraints for address with only country", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "CA",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test with only country
|
||||||
|
const shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "CA",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle hierarchical geo zone matching correctly", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test with full address - should match all levels
|
||||||
|
let shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
|
||||||
|
// Test with partial address - should still match broader zones
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
|
||||||
|
// Test with only country - should match country zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not match zones when address doesn't satisfy requirements", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Wrong postal code - should not match
|
||||||
|
let shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90211",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
|
||||||
|
// Wrong city - should not match
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "San Francisco",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
|
||||||
|
// Wrong province - should not match
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
|
||||||
|
// Wrong country - should not match
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle partial address matching correctly", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Full address with matching province should match
|
||||||
|
let shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Any City",
|
||||||
|
postal_expression: "12345",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
|
||||||
|
// Minimal matching address should match
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
|
||||||
|
// Address with only country should not match province-level zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle empty or null address fields correctly", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Address with undefined fields should still match if country matches
|
||||||
|
const shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: undefined,
|
||||||
|
city: undefined,
|
||||||
|
postal_expression: undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should correctly match addresses across multiple service zones", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "US Zones",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
city: "New York",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Europe Zones",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "FR",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "DE",
|
||||||
|
province_code: "BY",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Canada Zones",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "ON",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "BC",
|
||||||
|
city: "Vancouver",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create shipping options for each zone
|
||||||
|
const [usOption, europeOption, canadaOption] = await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "US Shipping",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "Europe Shipping",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[1].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "Canada Shipping",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[2].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test US ZIP code - should only match US zone
|
||||||
|
let shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(usOption.id)
|
||||||
|
|
||||||
|
// Test New York city - should only match US zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
city: "New York",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(usOption.id)
|
||||||
|
|
||||||
|
// Test France - should only match Europe zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "FR",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(europeOption.id)
|
||||||
|
|
||||||
|
// Test German province - should only match Europe zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "DE",
|
||||||
|
province_code: "BY",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(europeOption.id)
|
||||||
|
|
||||||
|
// Test Canadian province - should only match Canada zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "ON",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(canadaOption.id)
|
||||||
|
|
||||||
|
// Test Vancouver - should only match Canada zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "BC",
|
||||||
|
city: "Vancouver",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(canadaOption.id)
|
||||||
|
|
||||||
|
// Test non-matching address - should return no options
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "JP",
|
||||||
|
city: "Tokyo",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle overlapping zones across multiple service zones", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "Broad US Zone",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Specific California Zone",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Specific LA Zone",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create shipping options with different prices for each zone
|
||||||
|
const [broadOption, californiaOption, laOption] = await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "Standard US Shipping",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "California Express",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[1].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "LA Premium",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[2].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test LA ZIP code - should match all three zones
|
||||||
|
let shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(3)
|
||||||
|
const laIds = shippingOptions.map(opt => opt.id)
|
||||||
|
expect(laIds).toContain(broadOption.id)
|
||||||
|
expect(laIds).toContain(californiaOption.id)
|
||||||
|
expect(laIds).toContain(laOption.id)
|
||||||
|
|
||||||
|
// Test California (not LA) - should match broad US and California zones
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "San Francisco",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(2)
|
||||||
|
const caIds = shippingOptions.map(opt => opt.id)
|
||||||
|
expect(caIds).toContain(broadOption.id)
|
||||||
|
expect(caIds).toContain(californiaOption.id)
|
||||||
|
expect(caIds).not.toContain(laOption.id)
|
||||||
|
|
||||||
|
// Test US (not California) - should only match broad US zone
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(broadOption.id)
|
||||||
|
|
||||||
|
// Test non-US address - should match nothing
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "CA",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle mixed granularity zones across service zones", async () => {
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "Mixed Zone 1",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "FR",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mixed Zone 2",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
city: "New York",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "ON",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [option1, option2] = await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "Mixed Option 1",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
name: "Mixed Option 2",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[1].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Test LA ZIP - should match zone 1
|
||||||
|
let shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
city: "Los Angeles",
|
||||||
|
postal_expression: "90210",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(option1.id)
|
||||||
|
|
||||||
|
// Test France - should match zone 1
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "FR",
|
||||||
|
city: "Paris",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(option1.id)
|
||||||
|
|
||||||
|
// Test New York - should match zone 2
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
city: "New York",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(option2.id)
|
||||||
|
|
||||||
|
// Test Ontario, Canada - should match zone 2
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "ON",
|
||||||
|
city: "Toronto",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(1)
|
||||||
|
expect(shippingOptions[0].id).toBe(option2.id)
|
||||||
|
|
||||||
|
// Test unmatched location
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "UK",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("on update shipping option rules", () => {
|
describe("on update shipping option rules", () => {
|
||||||
it("should update a shipping option rule", async () => {
|
it("should update a shipping option rule", async () => {
|
||||||
const shippingProfile = await service.createShippingProfiles({
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
|
import { isObject } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
Fulfillment,
|
Fulfillment,
|
||||||
FulfillmentProvider,
|
FulfillmentProvider,
|
||||||
@@ -59,7 +60,6 @@ import { joinerConfig } from "../joiner-config"
|
|||||||
import { UpdateShippingOptionsInput } from "../types/service"
|
import { UpdateShippingOptionsInput } from "../types/service"
|
||||||
import { buildCreatedShippingOptionEvents } from "../utils/events"
|
import { buildCreatedShippingOptionEvents } from "../utils/events"
|
||||||
import FulfillmentProviderService from "./fulfillment-provider"
|
import FulfillmentProviderService from "./fulfillment-provider"
|
||||||
import { isObject } from "@medusajs/utils"
|
|
||||||
|
|
||||||
const generateMethodForModels = {
|
const generateMethodForModels = {
|
||||||
FulfillmentSet,
|
FulfillmentSet,
|
||||||
@@ -1411,7 +1411,7 @@ export default class FulfillmentModuleService
|
|||||||
shippingOption.id
|
shippingOption.id
|
||||||
)! // Guaranteed to exist since the validation above have been performed
|
)! // Guaranteed to exist since the validation above have been performed
|
||||||
|
|
||||||
if (isObject(shippingOption.type) && !("id" in shippingOption.type)) {
|
if (isObject(shippingOption.type) && !("id" in shippingOption.type)) {
|
||||||
optionTypeDeletedIds.push(existingShippingOption.type.id)
|
optionTypeDeletedIds.push(existingShippingOption.type.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1696,11 +1696,18 @@ export default class FulfillmentModuleService
|
|||||||
idOrSelector: string | FulfillmentTypes.FilterableShippingOptionTypeProps,
|
idOrSelector: string | FulfillmentTypes.FilterableShippingOptionTypeProps,
|
||||||
data: FulfillmentTypes.UpdateShippingOptionTypeDTO,
|
data: FulfillmentTypes.UpdateShippingOptionTypeDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<FulfillmentTypes.ShippingOptionTypeDTO[] | FulfillmentTypes.ShippingOptionTypeDTO> {
|
): Promise<
|
||||||
|
| FulfillmentTypes.ShippingOptionTypeDTO[]
|
||||||
|
| FulfillmentTypes.ShippingOptionTypeDTO
|
||||||
|
> {
|
||||||
let normalizedInput: FulfillmentTypes.UpdateShippingOptionTypeDTO[] = []
|
let normalizedInput: FulfillmentTypes.UpdateShippingOptionTypeDTO[] = []
|
||||||
if (isString(idOrSelector)) {
|
if (isString(idOrSelector)) {
|
||||||
// Check if the type exists in the first place
|
// Check if the type exists in the first place
|
||||||
await this.shippingOptionTypeService_.retrieve(idOrSelector, {}, sharedContext)
|
await this.shippingOptionTypeService_.retrieve(
|
||||||
|
idOrSelector,
|
||||||
|
{},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
normalizedInput = [{ id: idOrSelector, ...data }]
|
normalizedInput = [{ id: idOrSelector, ...data }]
|
||||||
} else {
|
} else {
|
||||||
const types = await this.shippingOptionTypeService_.list(
|
const types = await this.shippingOptionTypeService_.list(
|
||||||
@@ -2486,15 +2493,22 @@ export default class FulfillmentModuleService
|
|||||||
* Define the hierarchy of required properties for the geo zones.
|
* Define the hierarchy of required properties for the geo zones.
|
||||||
*/
|
*/
|
||||||
const geoZoneRequirePropertyHierarchy = {
|
const geoZoneRequirePropertyHierarchy = {
|
||||||
postal_expression: [
|
postal_expression: {
|
||||||
"country_code",
|
props: ["country_code", "province_code", "city", "postal_expression"],
|
||||||
"province_code",
|
type: "zip",
|
||||||
"city",
|
},
|
||||||
"postal_expression",
|
city: {
|
||||||
],
|
props: ["country_code", "province_code", "city"],
|
||||||
city: ["country_code", "province_code", "city"],
|
type: "city",
|
||||||
province_code: ["country_code", "province_code"],
|
},
|
||||||
country_code: ["country_code"],
|
province_code: {
|
||||||
|
props: ["country_code", "province_code"],
|
||||||
|
type: "province",
|
||||||
|
},
|
||||||
|
country_code: {
|
||||||
|
props: ["country_code"],
|
||||||
|
type: "country",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2504,18 +2518,21 @@ export default class FulfillmentModuleService
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const geoZoneConstraints = Object.entries(geoZoneRequirePropertyHierarchy)
|
const geoZoneConstraints = Object.entries(geoZoneRequirePropertyHierarchy)
|
||||||
.map(([prop, requiredProps]) => {
|
.map(([prop, { props, type }]) => {
|
||||||
if (address![prop]) {
|
if (address![prop]) {
|
||||||
return requiredProps.reduce((geoZoneConstraint, prop) => {
|
return {
|
||||||
if (isPresent(address![prop])) {
|
type,
|
||||||
geoZoneConstraint[prop] = address![prop]
|
...props.reduce((geoZoneConstraint, prop) => {
|
||||||
}
|
if (isPresent(address![prop])) {
|
||||||
return geoZoneConstraint
|
geoZoneConstraint[prop] = address![prop]
|
||||||
}, {} as Record<string, string | undefined>)
|
}
|
||||||
|
return geoZoneConstraint
|
||||||
|
}, {} as Record<string, string | undefined>),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
.filter((v): v is Record<string, any> => !!v)
|
.filter((v) => !!v)
|
||||||
|
|
||||||
return geoZoneConstraints
|
return geoZoneConstraints
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user