Skip to content

Commit

Permalink
Merge pull request #79 from yalla-coop/191-support-products-without-w…
Browse files Browse the repository at this point in the history
…holesale-mapping

191 - Support products  from the producer without a wholesale mapping
  • Loading branch information
dupreesi authored Jul 31, 2024
2 parents 54107de + 34f1b74 commit eb4f370
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 14 deletions.
114 changes: 114 additions & 0 deletions web/connector/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,120 @@ export function exportedDFCProducerProducts(stockLimitation = 55) {
}`;
}

export const exportedSingleTransformlessProducts = `{
"@context": "https://www.datafoodconsortium.org",
"@graph": [
{
"@id": "_:b228",
"@type": "dfc-b:QuantitativeValue",
"dfc-b:hasUnit": "dfc-m:Kilogram",
"dfc-b:value": "1"
},
{
"@id": "_:b229",
"@type": "dfc-b:Price",
"dfc-b:VATrate": "0",
"dfc-b:hasUnit": "dfc-m:Euro",
"dfc-b:value": "5.99"
},
{
"@id": "_:b230",
"@type": "dfc-b:QuantitativeValue",
"dfc-b:hasUnit": "dfc-m:Kilogram",
"dfc-b:value": "6"
},
{
"@id": "_:b231",
"@type": "dfc-b:Price",
"dfc-b:VATrate": "0",
"dfc-b:hasUnit": "dfc-m:Euro",
"dfc-b:value": "27.00"
},
{
"@id": "_:b232",
"@type": "dfc-b:QuantitativeValue",
"dfc-b:hasUnit": "dfc-m:Kilogram",
"dfc-b:value": "3"
},
{
"@id": "_:b233",
"@type": "dfc-b:Price",
"dfc-b:VATrate": "0",
"dfc-b:hasUnit": "dfc-m:Euro",
"dfc-b:value": "15.99"
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236913913",
"@type": "dfc-b:SuppliedProduct",
"dfc-b:description": "<span>hello</span>",
"dfc-b:hasQuantity": "_:b228",
"dfc-b:image": "https://cdn.shopify.com/s/files/1/0694/6491/6217/products/image_5611ad7e-65bf-4c16-b54f-9086acff0892.jpg?v=1710254054",
"dfc-b:name": "Botanical Flour, #2 Meadow Blend - Catering, kilo, 1kg",
"dfc-b:referencedBy": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236913913/CatalogItem"
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236913913/CatalogItem",
"@type": "dfc-b:CatalogItem",
"dfc-b:offeredThrough": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236913913/Offer",
"dfc-b:sku": "NFB2/1K",
"dfc-b:stockLimitation": "-3"
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236913913/Offer",
"@type": "dfc-b:Offer",
"dfc-b:hasPrice": {
"@id": "_:b229"
}
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236946681",
"@type": "dfc-b:SuppliedProduct",
"dfc-b:description": "<span>hello</span>",
"dfc-b:hasQuantity": "_:b230",
"dfc-b:image": "https://cdn.shopify.com/s/files/1/0694/6491/6217/products/image_5611ad7e-65bf-4c16-b54f-9086acff0892.jpg?v=1710254054",
"dfc-b:name": "Botanical Flour, #2 Meadow Blend - Case, 6 x 1kg",
"dfc-b:referencedBy": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236946681/CatalogItem"
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236946681/CatalogItem",
"@type": "dfc-b:CatalogItem",
"dfc-b:offeredThrough": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236946681/Offer",
"dfc-b:sku": "NFB2/C6",
"dfc-b:stockLimitation": "-2"
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236946681/Offer",
"@type": "dfc-b:Offer",
"dfc-b:hasPrice": {
"@id": "_:b231"
}
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236979449",
"@type": "dfc-b:SuppliedProduct",
"dfc-b:description": "<span>hello</span>",
"dfc-b:hasQuantity": "_:b232",
"dfc-b:image": "https://cdn.shopify.com/s/files/1/0694/6491/6217/products/image_5611ad7e-65bf-4c16-b54f-9086acff0892.jpg?v=1710254054",
"dfc-b:name": "Botanical Flour, #2 Meadow Blend - Catering, small, 3kg",
"dfc-b:referencedBy": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236979449/CatalogItem"
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236979449/CatalogItem",
"@type": "dfc-b:CatalogItem",
"dfc-b:offeredThrough": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236979449/Offer",
"dfc-b:sku": "NFB2/3K",
"dfc-b:stockLimitation": "-7"
},
{
"@id": "http://localhost:3629/api/dfc/Enterprises/alex-fdc-producer/SuppliedProducts/44518236979449/Offer",
"@type": "dfc-b:Offer",
"dfc-b:hasPrice": {
"@id": "_:b233"
}
}
]
}`;

export const importedShopifyProductsFromDFC = [
{
parentProduct: {
Expand Down
46 changes: 33 additions & 13 deletions web/connector/productUtils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getTargetStringFromSemanticId, throwError } from '../utils/index.js';

import { loadConnectorWithResources } from './index.js';
import { loadConnectorWithResources, SuppliedProduct } from './index.js';
import { loadProductTypes, loadQuantityUnits } from './mappings.js';

async function getSingleSuppliedProduct(suppliedProduct) {
Expand Down Expand Up @@ -104,17 +104,33 @@ async function importSuppliedProducts(dfcProducts) {
}
}

function findUnmappedSuppliedProducts(graph, retailWholesaleProductIds) {
return graph.filter(
(item) => item instanceof SuppliedProduct && !retailWholesaleProductIds.has(getTargetStringFromSemanticId(item.getSemanticId(), 'SuppliedProducts'))
);
}

async function getSuppliedProductDetailsFromImports(dfcExportsArray) {
const dfcImports = await importSuppliedProducts(dfcExportsArray);

const dfcRetailWholesalePairs = dfcImports.filter(
(item) => item.getSemanticType() === 'dfc-b:AsPlannedTransformation'
);

return await Promise.all(dfcRetailWholesalePairs.map(toShopifyProduct));
const retailWholesalePairs = await Promise.all(dfcRetailWholesalePairs.map(retailWholesaleTransformationToShopifyProduct));

const retailWholesaleProductIds = new Set(retailWholesalePairs.flatMap(({ retailProduct, wholesaleProduct }) => ([retailProduct.id, wholesaleProduct.id])));

const unMappedProducts = await Promise.all(findUnmappedSuppliedProducts(dfcImports, retailWholesaleProductIds).map(suppliedProductToShopifyProduct));

return [...retailWholesalePairs, ...unMappedProducts];
}

async function suppliedProductToShopifyProduct(suppliedProduct) {
return await createResultObject(suppliedProduct, null, 1);
}

async function toShopifyProduct(retailWholesalePair) {
async function retailWholesaleTransformationToShopifyProduct(retailWholesalePair) {
const consumptionFlows =
await retailWholesalePair.getPlannedConsumptionFlows();
const productionFlows = await retailWholesalePair.getPlannedProductionFlows();
Expand All @@ -129,25 +145,29 @@ async function toShopifyProduct(retailWholesalePair) {
const wholesaleProduct = await productionFlows[0].getProducedProduct();
const retailProduct = await consumptionFlows[0].getConsumedProduct();

const retailShopifyProduct =
await getSingleVariantSuppliedProduct(retailProduct);
const wholesaleShopifyProduct =
await getSingleVariantSuppliedProduct(wholesaleProduct);
const itemsPerWholesaleVariant = await await consumptionFlows[0]
.getQuantity()
.getQuantityValue();

return await createResultObject(retailProduct, wholesaleProduct, itemsPerWholesaleVariant);
}

async function createResultObject(retailProduct, wholesaleProduct, itemsPerWholesaleVariant) {
const retailShopifyProduct =
await getSingleVariantSuppliedProduct(retailProduct);
const wholesaleShopifyProduct = wholesaleProduct ? await getSingleVariantSuppliedProduct(wholesaleProduct) : retailShopifyProduct;

const parentShopifyProduct = await getSingleSuppliedProduct(retailProduct);
parentShopifyProduct.variants = [retailShopifyProduct];

parentShopifyProduct.images = retailShopifyProduct.image
? [
createImageObject(
retailShopifyProduct.image,
retailShopifyProduct.id,
1
)
]
createImageObject(
retailShopifyProduct.image,
retailShopifyProduct.id,
1
)
]
: [];

return {
Expand Down
16 changes: 15 additions & 1 deletion web/connector/productUtils.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
exportedDFCProducerProducts,
importedShopifyProductsFromDFC
importedShopifyProductsFromDFC,
exportedSingleTransformlessProducts
} from './mocks';
import { generateShopifyFDCProducts } from './productUtils';

Expand All @@ -24,4 +25,17 @@ describe('generateShopifyFDCProducts', () => {
expect(variant.inventoryQuantity).toEqual(0);
expect(variant.inventoryPolicy).toEqual('continue');
});

it('Can import single products that dont appear on transformations', async () => {
const result = await generateShopifyFDCProducts(exportedSingleTransformlessProducts);

expect(result).toHaveLength(3);

expect(result[0].retailProduct.title).toEqual('Botanical Flour, #2 Meadow Blend - Catering, kilo, 1kg');
expect(result[0].wholesaleProduct.title).toEqual('Botanical Flour, #2 Meadow Blend - Catering, kilo, 1kg');
expect(result[1].retailProduct.title).toEqual('Botanical Flour, #2 Meadow Blend - Case, 6 x 1kg');
expect(result[1].wholesaleProduct.title).toEqual('Botanical Flour, #2 Meadow Blend - Case, 6 x 1kg');
expect(result[2].retailProduct.title).toEqual('Botanical Flour, #2 Meadow Blend - Catering, small, 3kg');
expect(result[2].wholesaleProduct.title).toEqual('Botanical Flour, #2 Meadow Blend - Catering, small, 3kg');
}, 15000)
});

0 comments on commit eb4f370

Please sign in to comment.