punchout-catalogs / punchout-catalog-spryker
Spryker电子商务平台Punchout目录模块
Requires
- php: >=7.1
- ext-json: *
- ext-mbstring: *
- ext-simplexml: *
- spryker/cart: ^5.0.0 || ^7.0.0
- spryker/company-business-unit: ^2.9.0
- spryker/company-user: ^2.7.0
- spryker/customer: ^7.0.0
- spryker/data-import: ^1.6.0
- spryker/glossary: ^3.0.0
- spryker/glossary-storage: ^1.0.0
- spryker/kernel: ^3.33.0
- spryker/money: ^2.6.0
- spryker/oauth-company-user: ^2.0.0
- spryker/product-storage: ^1.11.0
- spryker/quote: ^2.0.0
- spryker/store: ^1.1.0
- spryker/util-uuid-generator: ^1.0.0
- spryker/vault: ^1.1.0
- spryker/zed-request: ^3.6.0
Requires (Dev)
Suggests
- spryker/product-bundle: Use at least ^6.0.0 to have ProductBundleCartItemTransformerPlugin
- 2.4.2
- 2.4.1
- 2.3.0
- 2.2.1
- 2.2.0
- 2.1.1
- 2.1.0
- 2.0.1
- 2.0.0
- 1.1.2
- 1.1.1
- 1.1.0
- dev-master / 1.0.x-dev
- 1.0.1
- 1.0.0
- dev-master-2021-dec-07
- dev-bugfix/support-release-202001
- dev-master-2021-nov-25
- dev-feature/POC-2628-price-per-quantity
- dev-feature/ps-8578/dev-punchout-catalog-cart-bc
- dev-fixes/db-structure-fix
- dev-feature/ps-8578/dev-punchout-catalog-architecture-overview
- dev-feature/ps-8578/dev-punchout-catalog
This package is auto-updated.
Last update: 2024-09-11 01:07:19 UTC
README
Spryker电子商务平台Punchout目录模块
重要变更
从2.4.0版本开始,更改了Punchout连接加载的方式。
#1.业务单元在网关URL(入口点)中变为可选。
#2.在cXML设置请求中需要输入买家ID,格式如下:Credential_Domain_Value/Credential_Identity_Value
。
示例 #1: AribaNetworkId/AN119990XX
示例 #2: NetworkId/NID119990XX
安装
composer require punchout-catalogs/punchout-catalog-spryker
B2C商店还需要spryker-feature/company-account
功能。
查看etc/integration-sample/*.patch
补丁作为与Spryker B2B 和 B2C 示例商店集成的示例。
文档
测试
运行
./vendor/bin/codecept run
自定义购物车映射
可以通过重写PunchoutCatalog\Yves\PunchoutCatalog\PunchoutCatalogConfig类的方法来实现扩展购物车映射行为
<?php namespace Pyz\Yves\PunchoutCatalog; use PunchoutCatalog\Yves\PunchoutCatalog\PunchoutCatalogConfig as BasePunchoutCatalogConfig; class PunchoutCatalogConfig extends BasePunchoutCatalogConfig { /** * @return array */ public function getCustomCartMapping(): array { return [ // QuoteTransfer => PunchoutCatalogDocumentCartTransfer // without key, should return transfer object function ($quoteTransfer, $cartRequestTransfer, $plugin) { $cartRequestTransfer->setCoupon('Coupon for ' . $quoteTransfer->getName()); return $cartRequestTransfer; }, 'cart_note' => 'name', ]; } /** * @return array */ public function getCustomCartItemMapping(): array { return [ //ItemTransfer => PunchoutCatalogDocumentCartItemTransfer 'custom_sku' => function($quoteItemTransfer, $documentCartItemTransfer, $quoteTransfer, $plugin) { return 'here-is-custom-sku-' . $quoteItemTransfer->getAbstractSku(); }, 'sale_bunch_quantity' => function($quoteItemTransfer, $documentCartItemTransfer, $quoteTransfer, $plugin) { //Product #1 if ($quoteItemTransfer->getAbstractSku() === 'any_condition_1') { return 100; } //Product #2 if ($quoteItemTransfer->getAbstractSku() === 'any_condition_2') { return 50; } return 1; }, 'custom_fields' => function($quoteItemTransfer, $documentCartItemTransfer, $quoteTransfer, $plugin) { return array( 'custom_field_1' => 'quote-item-id=' . $quoteItemTransfer->getId(), 'custom_field_2' => 'custom-abstract-sku-' . $quoteItemTransfer->getAbstractSku(), 'custom_field_3' => 'custom_field_value_3', 'custom_field_4' => 'custom_field_value_4_' . uniqid(), 'custom_field_5' => 'custom_field_value_5_' . uniqid(), 'custom_field_contract' => 'ContractID-'. uniqid(), 'custom_field_org' => 'TestPurchOrg', 'custom_field_ref' => 'some-ref', //...add as many custom fields as you need and can use in mapping ); }, /** * @param \Generated\Shared\Transfer\ItemTransfer * @param \Generated\Shared\Transfer\PunchoutCatalogDocumentCartItemTransfer * @param \Generated\Shared\Transfer\QuoteTransfer * @param \PunchoutCatalog\Yves\PunchoutCatalog\Mapper\CartTransferMapperDefaultPlugin */ function ($quoteItemTransfer, $documentCartItemTransfer, $quoteTransfer, $plugin) { $name = trim($quoteItemTransfer->getName()); $documentCartItemTransfer->setDiscountDescription('Custom discount description for ' . $name); return $documentCartItemTransfer; }, 'discount_description' => 'name', 'cart_note' => 'group_key', ]; } /** * @return array */ public function getCustomCartCustomerMapping(): array { return [ //CustomerTransfer => PunchoutCatalogDocumentCartCustomerTransfer 'first_name' => 'customer_reference', /** * @param \Generated\Shared\Transfer\CustomerTransfer * @param \Generated\Shared\Transfer\PunchoutCatalogDocumentCustomerTransfer * @param \Generated\Shared\Transfer\QuoteTransfer * @param \PunchoutCatalog\Yves\PunchoutCatalog\Mapper\CartTransferMapperDefaultPlugin */ function ($quoteCustomerTransfer, $documentCartCustomerTransfer, $quoteTransfer, $plugin) { return $documentCartCustomerTransfer; }, ]; } }
如果这个机会还不够,您可以定义自己的插件,该插件应实现PunchoutCatalog\Yves\PunchoutCatalog\Mapper\CartTransferMapperPluginInterface
,并通过重写PunchoutCatalog\Yves\PunchoutCatalog\PunchoutCatalogDependencyProvider::getCartTransferMapperPlugins
方法来添加它。
以新方式启用Yves控制器(自Spryker版本202001
以来)
在src/Pyz/Yves/Router/RouterDependencyProvider.php
中启用Yves Punchout路由
<?php namespace Pyz\Yves\Router; use PunchoutCatalog\Yves\PunchoutCatalog\Plugin\Router\PunchoutCatalogRouteProviderPlugin; use Spryker\Yves\Router\RouterDependencyProvider as SprykerRouterDependencyProvider; class RouterDependencyProvider extends SprykerRouterDependencyProvider { /** * @return \Spryker\Yves\RouterExtension\Dependency\Plugin\RouteProviderPluginInterface[] */ protected function getRouteProvider(): array { return [ new PunchoutCatalogRouteProviderPlugin() ]; } }
以旧方式启用Yves控制器(Spryker版本202001
之前)
在src/Pyz/Yves/ShopApplication/YvesBootstrap.php
中注册Punchout路由
<?php namespace Pyz\Yves\ShopApplication; use PunchoutCatalog\Yves\PunchoutCatalog\Plugin\Provider\PunchoutCatalogControllerProvider; use SprykerShop\Yves\ShopApplication\YvesBootstrap as SprykerYvesBootstrap; class YvesBootstrap extends SprykerYvesBootstrap { /** * @param bool|null $isSsl * * @return \SprykerShop\Yves\ShopApplication\Plugin\Provider\AbstractYvesControllerProvider[] */ protected function getControllerProviderStack($isSsl) { return [ new PunchoutCatalogControllerProvider($isSsl), ]; } }
以新方式启用Zed控制器(自Spryker版本202001
以来)
在src/Pyz/Zed/Router/RouterConfig.php
中启用Zed Punchout路由
<?php namespace Pyz\Zed\Router; use Spryker\Zed\Router\RouterConfig as SprykerRouterConfig; class RouterConfig extends SprykerRouterConfig { /** * @return string[] */ public function getControllerDirectories(): array { $controllerDirectories = parent::getControllerDirectories(); //... $controllerDirectories[] = sprintf('%s/punchout-catalogs/*/src/*/Zed/*/Communication/Controller/', APPLICATION_VENDOR_DIR); return array_filter($controllerDirectories, 'glob'); } }
故障排除
创建认证令牌错误的问题
如果spy_oauth_access_token.user_identifier
字段太小,无法存储字段中的数据,则可能发生punchout-catalog.error.auth.token.create
错误。默认为varchar(1024)
。
解决方案
最简单的方法是将其从varchar(1024)
升级到LONGVARCHAR
。
创建方案文件 #1: src/Pyz/Zed/PunchoutCatalog/Persistence/Propel/Schema/spy_oauth.schema.xml
<?xml version="1.0"?> <database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="zed" xsi:noNamespaceSchemaLocation="http://static.spryker.com/schema-01.xsd" namespace="Orm\Zed\Oauth\Persistence" package="src.Orm.Zed.Oauth.Persistence"> <table name="spy_oauth_access_token"> <column name="user_identifier" type="LONGVARCHAR"/> </table> </database>
创建方案文件 #2: src/Pyz/Zed/PunchoutCatalog/Persistence/Propel/Schema/spy_oauth_revoke.schema.xml
<?xml version="1.0"?> <database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="zed" xsi:noNamespaceSchemaLocation="http://static.spryker.com/schema-01.xsd" namespace="Orm\Zed\OauthRevoke\Persistence" package="src.Orm.Zed.OauthRevoke.Persistence"> <table name="spy_oauth_refresh_token"> <column name="user_identifier" type="LONGVARCHAR"/> </table> </database>
数据库升级
vendor/bin/console propel:install
在管理员面板中消失的PunchOut
菜单项问题(与spryker-eco/punchout-catalogs
相关)
可能原因
使用BREADCRUMB_MERGE_STRATEGY
隐藏了所有未在config/Zed/navigation.xml
文件中定义的自定义菜单项。请参阅:https://docs.spryker.com/docs/scos/dev/back-end-development/extending-spryker/adding-navigation-in-the-back-office.html#defining-a-navigation-merge-strategy 在src/Pyz/Zed/ZedNavigation/ZedNavigationConfig.php
文件中定义的策略。
解决方案
通过在config/Zed/navigation.xml
文件中添加以下代码轻松恢复BREADCRUMB_MERGE_STRATEGY
的菜单项
<punchout-catalogs>
<label>PunchOut</label>
<title>PunchOut</title>
<pages>
<connection>
<label>Connections</label>
<title>Connections</title>
<bundle>punchout-catalogs</bundle>
<controller>index</controller>
<action>index</action>
<visible>1</visible>
</connection>
<transaction-log>
<label>Transactions Log</label>
<title>Transactions Log</title>
<bundle>punchout-catalogs</bundle>
<controller>transaction</controller>
<action>index</action>
<visible>1</visible>
</transaction-log>
</pages>
</punchout-catalogs>
如果导航菜单已缓存(商店以production
模式运行),请运行application:build-navigation-cache
命令。
具有许多自定义字段的OCI购物车映射示例
{ "cart_item": { "fields": { "quantity": { "path": "NEW_ITEM-QUANTITY[%line_number%]" }, "internal_id": { "path": "NEW_ITEM-EXT_PRODUCT_ID[%line_number%]" }, "parent_line_number": { "path": "NEW_ITEM-PARENT_ID[%line_number%]" }, "item_type": { "path": "NEW_ITEM-ITEM_TYPE[%line_number%]", "transform": [ { "map": { "value": "composite", "result": "R" } }, { "map": { "value": "item", "result": "O" } } ] }, "sku": { "path": "NEW_ITEM-VENDORMAT[%line_number%],NEW_ITEM-MANUFACTMAT[%line_number%]" }, "currency": { "path": "NEW_ITEM-CURRENCY[%line_number%]" }, "unit_total": { "path": "NEW_ITEM-PRICE[%line_number%]" }, "name": { "path": "NEW_ITEM-DESCRIPTION[%line_number%]", "transform": [{ "cut": { "len": "40" } }] }, "long_description": { "path": "NEW_ITEM-LONGTEXT_%line_number%:132[]" }, "uom": { "path": "NEW_ITEM-UNIT[%line_number%]", "transform": [{ "default": { "value": "EA" } }] }, "unspsc": { "path": "NEW_ITEM-MATGROUP[%line_number%]" }, "supplier_id": { "path": "NEW_ITEM-VENDOR[%line_number%]" }, "sale_bunch_quantity": { "path": "NEW_ITEM-PRICEUNIT[%line_number%]" }, "custom_fields/custom_field_org": { "path": "NEW_ITEM-PURCHORG[%line_number%]" }, "custom_fields/custom_field_ref": { "path": "NEW_ITEM-PURCHINFREC[%line_number%]", "transform": [ "uppercase" ] }, "custom_fields/custom_field_contract": { "path": "NEW_ITEM-CONTRACT[%line_number%]", "transform": [ "lowercase" ] }, "custom_fields/custom_field_1": { "path": "NEW_ITEM-CUSTFIELD1[%line_number%]" }, "custom_fields/custom_field_2": { "path": "NEW_ITEM-CUSTFIELD2[%line_number%]" }, "custom_fields/custom_field_3": { "path": "NEW_ITEM-CUSTFIELD3[%line_number%]" }, "custom_fields/custom_field_4": { "path": "NEW_ITEM-CUSTFIELD4[%line_number%]" }, "custom_fields/custom_field_5": { "path": "NEW_ITEM-CUSTFIELD5[%line_number%]" } } } }
OCI购物车映射示例,包含集成销售批量数量(以及新的项目-价格单位)
{ "cart_item": { "fields": { ... "quantity": { "path": "NEW_ITEM-QUANTITY[%line_number%]" }, "sale_bunch_quantity": { "path": "NEW_ITEM-PRICEUNIT[%line_number%]", "transform": [ "round" ] }, "unit_total": { "path": "NEW_ITEM-PRICE[%line_number%]" } } } }
OCI购物车映射示例,包含销售数量(不包含新的项目-价格单位,并分摊价格)
sale_quantity = 数量 / 销售批量数量
{ "cart_item": { "fields": { ... "sale_quantity": { "path": "NEW_ITEM-QUANTITY[%line_number%]" }, "sale_bunch_quantity_unit_total": { "path": "NEW_ITEM-PRICE[%line_number%]", "transform": [ {"round": {"precision": "3"}} ] } } } }
cXML购物车映射示例
{ "cart": { "fields": { "grand_total": { "path": "/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Total[1]/Money[1]" }, "tax_total": { "path": "/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Tax[1]/Money[1]" }, "tax_description": { "path": "/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Tax[1]/Description[1]" }, "discount_total": { "path": "/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Discount[1]/Money[1]" }, "discount_description": { "path": "/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Discount[1]/Description[1]" }, "currency": { "path": "/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Total[1]/Money[1]/@currency,/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Tax[1]/Money[1]/@currency,/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Discount[1]/Money[1]/@currency", "append": true }, "cart_note": { "path": "/cXML/Message[1]/PunchOutOrderMessage[1]/PunchOutOrderMessageHeader[1]/Comments[1]" } } }, "cart_item": { "fields": { "line_number": { "path": "@lineNumber" }, "parent_line_number": { "path": "@parentLineNumber" }, "item_type": { "path": "@itemType" }, "composite_item_type": { "path": "@compositeItemType" }, "quantity": { "path": "@quantity" }, "internal_id": { "path": "ItemID[1]/SupplierPartAuxiliaryID[1]" }, "sku": { "path": "ItemID[1]/SupplierPartID[1],ItemDetail[1]/BuyerPartID[1],ItemDetail[1]/ManufacturerPartID[1]" }, "unit_total": { "path": "ItemDetail[1]/UnitPrice[1]/Money[1]" }, "currency": { "path": "ItemDetail[1]/UnitPrice[1]/Money[1]/@currency" }, "name": { "path": "ItemDetail[1]/Description[1]/ShortName" }, "long_description": { "path": "ItemDetail[1]/Description[1]" }, "uom": { "path": "ItemDetail[1]/UnitOfMeasure[1]", "transform": [{ "default": { "value": "EA" } }] }, "brand": { "path": "ItemDetail[1]/ManufacturerName[1]" }, "supplier_id": { "path": "ItemDetail[1]/SupplierID[1]" }, "cart_note": { "path": "ItemDetail[1]/Comments[1]" }, "image_url": { "path": "ItemDetail[1]/Extrinsic[@name='ImageURL']" }, "locale": { "path": "ItemDetail[1]/Description[1]/@xml:lang" }, "options": { "path": "ItemDetail[1]/Extrinsic/customOption()", "multiple": true } } }, "customOption": { "fields": { "code": { "path": "@name" }, "value": { "path": "./" } } } }
cXML请求映射示例
{ "customer": { "fields": { "first_name": { "path": "/cXML/Request[1]/PunchOutSetupRequest[1]/Extrinsic[@name='FirstName']" }, "last_name": { "path": "/cXML/Request[1]/PunchOutSetupRequest[1]/Extrinsic[@name='LastName']" }, "email": { "path": "/cXML/Request[1]/PunchOutSetupRequest[1]/Extrinsic[@name='UserEmail']" } } }, "cart_item": { "fields": { "internal_id":{ "path": "/cXML/Request[1]/PunchOutSetupRequest[1]/ItemOut/ItemID[1]/SupplierPartAuxiliaryID" } } } }