diff --git a/packages/api-plugin-template.erxes/src/index.ts b/packages/api-plugin-template.erxes/src/index.ts index 02e7c67f37..9f4bcf3de3 100644 --- a/packages/api-plugin-template.erxes/src/index.ts +++ b/packages/api-plugin-template.erxes/src/index.ts @@ -704,3 +704,6 @@ async function startServer() { } startServer(); + + +// conflict test \ No newline at end of file diff --git a/packages/erxes-ui/src/brands/containers/SelectBrands.tsx b/packages/erxes-ui/src/brands/containers/SelectBrands.tsx index 571214f4cf..73da71d21f 100644 --- a/packages/erxes-ui/src/brands/containers/SelectBrands.tsx +++ b/packages/erxes-ui/src/brands/containers/SelectBrands.tsx @@ -22,6 +22,7 @@ export default ({ initialValue, multi = true, label, + customOption, name }: { queryParams?: IQueryParams; @@ -46,6 +47,7 @@ export default ({ generateOptions={generateBrandOptions} onSelect={onSelect} multi={multi} + customOption={customOption} /> ); -}; \ No newline at end of file +}; diff --git a/packages/erxes-ui/src/components/filter/Filter.tsx b/packages/erxes-ui/src/components/filter/Filter.tsx index a85cfb5021..35a786ef80 100644 --- a/packages/erxes-ui/src/components/filter/Filter.tsx +++ b/packages/erxes-ui/src/components/filter/Filter.tsx @@ -133,7 +133,14 @@ function Filter({ queryParams = {}, filterTitle, history }: IProps) { {renderFilterParam('status', false)} {renderFilterParam('state', false)} {renderFilterParam('categoryApprovalState', false)} - {renderFilterWithData('categoryId', 'forum')} + {location.href.includes('forum') && + renderFilterWithData('categoryId', 'forum')} + {location.href.includes('product') && + renderFilterWithData( + 'categoryId', + 'productCategory', + '_id, code, name' + )} {renderFilterParam('participating', true)} {renderFilterParam('unassigned', true)} {renderFilterParam('awaitingResponse', true, 'Awaiting Response')} diff --git a/packages/erxes-ui/src/components/filter/createChipText.tsx b/packages/erxes-ui/src/components/filter/createChipText.tsx index a584fd8710..e2b3f6c2b1 100644 --- a/packages/erxes-ui/src/components/filter/createChipText.tsx +++ b/packages/erxes-ui/src/components/filter/createChipText.tsx @@ -16,6 +16,7 @@ const ChipText = (props: any) => { const branch = query.branchDetail; const department = query.departmentDetail; const unit = query.unitDetail; + const productCategory = query.productCategoryDetail; return ( (brand && brand.name) || @@ -26,7 +27,8 @@ const ChipText = (props: any) => { (forum && forum.name) || (branch && branch.title) || (department && department.title) || - (unit && unit.title) + (unit && unit.title) || + (productCategory && `${productCategory.code} - ${productCategory.name}`) ); }; diff --git a/packages/plugin-pos-api/src/routes.ts b/packages/plugin-pos-api/src/routes.ts index 3d5ed9f6b2..448d52f9ff 100644 --- a/packages/plugin-pos-api/src/routes.ts +++ b/packages/plugin-pos-api/src/routes.ts @@ -5,7 +5,8 @@ import { IPosDocument } from './models/definitions/pos'; import { sendCoreMessage, sendPricingMessage, - sendProductsMessage + sendProductsMessage, + sendSyncerkhetMessage } from './messageBroker'; import { USER_FIELDS } from './contants'; @@ -45,7 +46,7 @@ export const getConfigData = async (subdomain: string, pos: IPosDocument) => { } if (pos.erkhetConfig && pos.erkhetConfig.isSyncErkhet) { - const configs = await sendCoreMessage({ + const configs = await sendSyncerkhetMessage({ subdomain, action: 'getConfig', data: { code: 'ERKHET', defaultValue: {} }, @@ -54,7 +55,7 @@ export const getConfigData = async (subdomain: string, pos: IPosDocument) => { data.pos.erkhetConfig = { ...pos.erkhetConfig, - getRemainderApiUrl: configs.getRemainderApiUrl, + url: process.env.ERKHET_URL, apiKey: configs.apiKey, apiSecret: configs.apiSecret, apiToken: configs.apiToken @@ -309,7 +310,12 @@ export const unfetchOrderInfo = async (req, res) => { const models = await generateModels(subdomain); const { orderId, token } = req.body; - let erkhetConfig = await getConfig(subdomain, 'ERKHET', {}); + let erkhetConfig = await sendSyncerkhetMessage({ + subdomain, + action: 'getConfig', + data: { code: 'ERKHET', defaultValue: {} }, + isRPC: true + }); if ( !erkhetConfig || diff --git a/packages/plugin-posclient-api/src/graphql/utils/products.ts b/packages/plugin-posclient-api/src/graphql/utils/products.ts index 7b84ecf7cd..3aee0ca473 100644 --- a/packages/plugin-posclient-api/src/graphql/utils/products.ts +++ b/packages/plugin-posclient-api/src/graphql/utils/products.ts @@ -14,12 +14,7 @@ export const checkRemainders = async ( if (config.erkhetConfig && config.erkhetConfig.getRemainder) { const configs = config.erkhetConfig; - if ( - configs && - configs.getRemainderApiUrl && - configs.apiKey && - configs.apiSecret - ) { + if (configs && configs.url && configs.apiKey && configs.apiSecret) { try { let account = configs.account; let location = configs.location; @@ -35,7 +30,8 @@ export const checkRemainders = async ( if (account && location) { const response = await sendRequest({ - url: configs.getRemainderApiUrl, + url: `${configs.url || + 'https://erkhet.biz'}/get-api/?kind=remainder`, method: 'GET', params: { kind: 'remainder', diff --git a/packages/plugin-products-api/src/dataloaders/resolvers/queries/products.ts b/packages/plugin-products-api/src/dataloaders/resolvers/queries/products.ts index a6ffaccf3a..b66793c4dd 100644 --- a/packages/plugin-products-api/src/dataloaders/resolvers/queries/products.ts +++ b/packages/plugin-products-api/src/dataloaders/resolvers/queries/products.ts @@ -21,6 +21,7 @@ interface IQueryParams { categoryId?: string; searchValue?: string; vendorId?: string; + brand?: string; tag: string; page?: number; perPage?: number; @@ -44,6 +45,7 @@ const generateFilter = async ( categoryId, searchValue, vendorId, + brand, tag, ids, excludeIds, @@ -131,6 +133,10 @@ const generateFilter = async ( filter.vendorId = vendorId; } + if (brand) { + filter.scopeBrandIds = { $in: [brand] }; + } + return filter; }; @@ -140,6 +146,7 @@ const generateFilterCat = async ({ withChild, searchValue, meta, + brand, status }) => { const filter: any = {}; @@ -170,6 +177,10 @@ const generateFilterCat = async ({ } } + if (brand) { + filter.scopeBrandIds = { $in: [brand] }; + } + if (meta) { if (!isNaN(meta)) { filter.meta = { $lte: Number(meta) }; @@ -391,7 +402,7 @@ const productQueries = { async productCategories( _root, - { parentId, withChild, searchValue, status, meta }, + { parentId, withChild, searchValue, status, brand, meta }, { models }: IContext ) { const filter = await generateFilterCat({ @@ -400,6 +411,7 @@ const productQueries = { parentId, withChild, searchValue, + brand, meta }); @@ -412,7 +424,7 @@ const productQueries = { async productCategoriesTotalCount( _root, - { parentId, searchValue, status, withChild, meta }, + { parentId, searchValue, status, withChild, brand, meta }, { models }: IContext ) { const filter = await generateFilterCat({ @@ -421,6 +433,7 @@ const productQueries = { withChild, searchValue, status, + brand, meta }); return models.ProductCategories.find(filter).countDocuments(); diff --git a/packages/plugin-products-api/src/graphql/schema/product.ts b/packages/plugin-products-api/src/graphql/schema/product.ts index b58aca80c9..ef9514cd9b 100644 --- a/packages/plugin-products-api/src/graphql/schema/product.ts +++ b/packages/plugin-products-api/src/graphql/schema/product.ts @@ -35,6 +35,7 @@ export const types = (tagsAvailable, contactsAvailable) => ` parentId: String code: String! order: String! + scopeBrandIds: [String] attachment: Attachment status: String isRoot: Boolean @@ -66,6 +67,7 @@ export const types = (tagsAvailable, contactsAvailable) => ` attachment: Attachment attachmentMore: [Attachment] vendorId: String + scopeBrandIds: [String] uom: String subUoms: JSON @@ -102,6 +104,7 @@ const productParams = ` attachment: AttachmentInput, attachmentMore: [AttachmentInput], vendorId: String, + scopeBrandIds: [String] uom: String, subUoms: JSON, taxType: String, @@ -114,6 +117,7 @@ const productCategoryParams = ` description: String, meta: String, parentId: String, + scopeBrandIds: [String] attachment: AttachmentInput, status: String maskType: String @@ -128,6 +132,7 @@ const productsQueryParams = ` categoryId: String, searchValue: String, vendorId: String, + brand: String tag: String, ids: [String], excludeIds: Boolean, @@ -139,7 +144,7 @@ const productsQueryParams = ` `; export const queries = ` - productCategories(parentId: String, withChild: Boolean, searchValue: String, status: String, meta: String): [ProductCategory] + productCategories(parentId: String, withChild: Boolean, searchValue: String, status: String, meta: String, brand: String): [ProductCategory] productCategoriesTotalCount(parentId: String, withChild: Boolean, searchValue: String, status: String, meta: String): Int productCategoryDetail(_id: String): ProductCategory products( diff --git a/packages/plugin-products-ui/src/components/product/ProductList.tsx b/packages/plugin-products-ui/src/components/product/ProductList.tsx index 5364277faa..761aa2c868 100755 --- a/packages/plugin-products-ui/src/components/product/ProductList.tsx +++ b/packages/plugin-products-ui/src/components/product/ProductList.tsx @@ -299,6 +299,7 @@ class List extends React.Component { header={ } diff --git a/packages/plugin-products-ui/src/components/productCategory/CategoryList.tsx b/packages/plugin-products-ui/src/components/productCategory/CategoryList.tsx index 929852ec89..be8996db1e 100644 --- a/packages/plugin-products-ui/src/components/productCategory/CategoryList.tsx +++ b/packages/plugin-products-ui/src/components/productCategory/CategoryList.tsx @@ -1,23 +1,25 @@ -import { __, router } from '@erxes/ui/src/utils'; +import { router, __ } from '@erxes/ui/src/utils'; -import Button from '@erxes/ui/src/components/Button'; import CategoryForm from '@erxes/ui-products/src/containers/CategoryForm'; -import CategoryStatusFilter from '../product/filters/CategoryStatusFilter'; -import CollapsibleList from '@erxes/ui/src/components/collapsibleList/CollapsibleList'; import { Header } from '@erxes/ui-settings/src/styles'; -import { IProductCategory } from '../../types'; +import BrandFilter from '@erxes/ui/src/brands/components/BrandFilter'; +import { IBrand } from '@erxes/ui/src/brands/types'; +import Button from '@erxes/ui/src/components/Button'; +import CollapsibleList from '@erxes/ui/src/components/collapsibleList/CollapsibleList'; import Icon from '@erxes/ui/src/components/Icon'; import ModalTrigger from '@erxes/ui/src/components/ModalTrigger'; -import ProductTypeFilter from '../product/filters/ProdcutTypeFilter'; -import React from 'react'; -import SegmentFilter from '../product/filters/SegmentFilter'; -import Sidebar from '@erxes/ui/src/layout/components/Sidebar'; -import { SidebarList } from '@erxes/ui/src/layout/styles'; -import TagFilter from '../../containers/TagFilter'; import Tip from '@erxes/ui/src/components/Tip'; +import Sidebar from '@erxes/ui/src/layout/components/Sidebar'; import Wrapper from '@erxes/ui/src/layout/components/Wrapper'; +import { SidebarList } from '@erxes/ui/src/layout/styles'; import { isEnabled } from '@erxes/ui/src/utils/core'; import { pluginsOfProductCategoryActions } from 'coreui/pluginUtils'; +import React from 'react'; +import TagFilter from '../../containers/TagFilter'; +import { IProductCategory } from '../../types'; +import CategoryStatusFilter from '../product/filters/CategoryStatusFilter'; +import ProductTypeFilter from '../product/filters/ProdcutTypeFilter'; +import SegmentFilter from '../product/filters/SegmentFilter'; const { Section } = Wrapper.Sidebar; @@ -28,6 +30,8 @@ interface IProps { productCategories: IProductCategory[]; productCategoriesCount: number; loading: boolean; + brands: IBrand[]; + brandsLoading: boolean; } class List extends React.Component { @@ -141,6 +145,11 @@ class List extends React.Component { )} + {isEnabled('tags') && } ); diff --git a/packages/plugin-products-ui/src/containers/product/ProductList.tsx b/packages/plugin-products-ui/src/containers/product/ProductList.tsx index 4eb5919350..219c262aa1 100644 --- a/packages/plugin-products-ui/src/containers/product/ProductList.tsx +++ b/packages/plugin-products-ui/src/containers/product/ProductList.tsx @@ -145,6 +145,7 @@ export default withProps( categoryId: queryParams.categoryId, status: queryParams.productStatus, tag: queryParams.tag, + brand: queryParams.brand, searchValue: queryParams.searchValue, type: queryParams.type, segment: queryParams.segment, diff --git a/packages/plugin-products-ui/src/containers/productCategory/CategoryList.tsx b/packages/plugin-products-ui/src/containers/productCategory/CategoryList.tsx index 0706c65b0e..527d022443 100644 --- a/packages/plugin-products-ui/src/containers/productCategory/CategoryList.tsx +++ b/packages/plugin-products-ui/src/containers/productCategory/CategoryList.tsx @@ -1,18 +1,18 @@ -import * as compose from 'lodash.flowright'; - +import { gql } from '@apollo/client'; +import { graphql } from '@apollo/client/react/hoc'; +import { ProductCategoriesQueryResponse } from '@erxes/ui-products/src/types'; +import { queries as brandQueries } from '@erxes/ui/src/brands/graphql'; +import { BrandsQueryResponse } from '@erxes/ui/src/brands/types'; import { Alert, confirm, withProps } from '@erxes/ui/src/utils'; +import * as compose from 'lodash.flowright'; +import React from 'react'; +import List from '../../components/productCategory/CategoryList'; +import { mutations, queries } from '../../graphql'; import { ProductCategoriesCountQueryResponse, ProductCategoryRemoveMutationResponse, ProductsQueryResponse } from '../../types'; -import { mutations, queries } from '../../graphql'; - -import List from '../../components/productCategory/CategoryList'; -import { ProductCategoriesQueryResponse } from '@erxes/ui-products/src/types'; -import React from 'react'; -import { gql } from '@apollo/client'; -import { graphql } from '@apollo/client/react/hoc'; type Props = { history: any; queryParams: any }; @@ -20,6 +20,7 @@ type FinalProps = { productCategoriesQuery: ProductCategoriesQueryResponse; productCategoriesCountQuery: ProductCategoriesCountQueryResponse; productsQuery: ProductsQueryResponse; + brandsQuery: BrandsQueryResponse; } & Props & ProductCategoryRemoveMutationResponse; class ProductListContainer extends React.Component { @@ -51,6 +52,10 @@ class ProductListContainer extends React.Component { }); }; + const { brandsQuery } = this.props; + const brands = (brandsQuery ? brandsQuery.brands : []) || []; + const brandsLoading = (brandsQuery && brandsQuery.loading) || false; + const productCategories = productCategoriesQuery.productCategories || []; const updatedProps = { @@ -59,7 +64,9 @@ class ProductListContainer extends React.Component { productCategories, loading: productCategoriesQuery.loading, productCategoriesCount: - productCategoriesCountQuery.productCategoriesTotalCount || 0 + productCategoriesCountQuery.productCategoriesTotalCount || 0, + brands, + brandsLoading }; return ; @@ -83,6 +90,7 @@ export default withProps( options: ({ queryParams }) => ({ variables: { status: queryParams.categoryStatus, + brand: queryParams.brand, parentId: queryParams.parentId }, refetchQueries: getRefetchQueries(), @@ -105,6 +113,9 @@ export default withProps( ), graphql(gql(queries.products), { name: 'productsQuery' + }), + graphql(gql(brandQueries.brands), { + name: 'brandsQuery' }) )(ProductListContainer) ); diff --git a/packages/plugin-syncerkhet-api/src/afterMutations.ts b/packages/plugin-syncerkhet-api/src/afterMutations.ts index 8abef9d4a7..fb8a627c84 100644 --- a/packages/plugin-syncerkhet-api/src/afterMutations.ts +++ b/packages/plugin-syncerkhet-api/src/afterMutations.ts @@ -4,9 +4,9 @@ import { productToErkhet, productCategoryToErkhet } from './utils/productToErkhet'; -import { getConfig, sendCardInfo } from './utils/utils'; import { customerToErkhet, companyToErkhet } from './utils/customerToErkhet'; import { generateModels } from './connectionResolver'; +import { getSyncLogDoc } from './utils/utils'; const allowTypes = { 'cards:deal': ['update'], @@ -21,16 +21,6 @@ export const afterMutationHandlers = async (subdomain, params) => { const models = await generateModels(subdomain); - const syncLogDoc = { - type: '', - contentType: type, - contentId: params.object._id, - createdAt: new Date(), - createdBy: user._id, - consumeData: params, - consumeStr: JSON.stringify(params) - }; - if (!Object.keys(allowTypes).includes(type)) { return; } @@ -39,113 +29,134 @@ export const afterMutationHandlers = async (subdomain, params) => { return; } - let syncLog; - - try { - if (type === 'cards:deal') { - if (action === 'update') { - const deal = params.updatedDocument; - const oldDeal = params.object; - const destinationStageId = deal.stageId || ''; + if (type === 'cards:deal') { + if (action === 'update') { + const deal = params.updatedDocument; + const oldDeal = params.object; + const destinationStageId = deal.stageId || ''; - if (!(destinationStageId && destinationStageId !== oldDeal.stageId)) { - return; - } + if (!(destinationStageId && destinationStageId !== oldDeal.stageId)) { + return; + } - const configs = await getConfig(subdomain, 'ebarimtConfig', {}); - const moveConfigs = await getConfig(subdomain, 'stageInMoveConfig', {}); - const returnConfigs = await getConfig( - subdomain, - 'returnEbarimtConfig', - {} + const saleConfigs = await models.Configs.getConfig( + 'stageInSaleConfig', + {} + ); + + const moveConfigs = await models.Configs.getConfig( + 'stageInMoveConfig', + {} + ); + const returnConfigs = await models.Configs.getConfig( + 'returnEbarimtConfig', + {} + ); + + const mainConfigs = await models.Configs.getConfig('erkhetConfig', {}); + + // return + if (Object.keys(returnConfigs).includes(destinationStageId)) { + const returnConfig = { + ...returnConfigs[destinationStageId], + ...(await models.Configs.getConfig('ERKHET', {})) + }; + + const orderInfos = [ + { + orderId: deal._id, + returnKind: 'note' + } + ]; + + const postData = { + userEmail: returnConfig.userEmail, + token: returnConfig.apiToken, + apiKey: returnConfig.apiKey, + apiSecret: returnConfig.apiSecret, + orderInfos: JSON.stringify(orderInfos) + }; + const syncLog = await models.SyncLogs.syncLogsAdd( + getSyncLogDoc(params) + ); + await sendRPCMessage( + models, + syncLog, + 'rpc_queue:erxes-automation-erkhet', + { + action: 'get-response-return-order', + isJson: true, + isEbarimt: false, + payload: JSON.stringify(postData), + thirdService: true + } ); - // return - if (Object.keys(returnConfigs).includes(destinationStageId)) { - syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); - const returnConfig = { - ...returnConfigs[destinationStageId], - ...(await getConfig(subdomain, 'ERKHET', {})) - }; + return; + } - const orderInfos = [ - { - orderId: deal._id, - returnKind: 'note' - } - ]; - - const postData = { - userEmail: returnConfig.userEmail, - token: returnConfig.apiToken, - apiKey: returnConfig.apiKey, - apiSecret: returnConfig.apiSecret, - orderInfos: JSON.stringify(orderInfos) - }; + // move + if (Object.keys(moveConfigs).includes(destinationStageId)) { + const moveConfig = { + ...moveConfigs[destinationStageId], + ...(await models.Configs.getConfig('ERKHET', {})) + }; - await sendRPCMessage( - models, - syncLog, - 'rpc_queue:erxes-automation-erkhet', - { - action: 'get-response-return-order', - isJson: true, - isEbarimt: false, - payload: JSON.stringify(postData), - thirdService: true - } - ); + const postData = await getMoveData(subdomain, moveConfig, deal); + const syncLog = await models.SyncLogs.syncLogsAdd( + getSyncLogDoc(params) + ); + const response = await sendRPCMessage( + models, + syncLog, + 'rpc_queue:erxes-automation-erkhet', + { + action: 'get-response-inv-movement-info', + isJson: true, + isEbarimt: false, + payload: JSON.stringify(postData), + thirdService: true + } + ); - return; + if (response.message || response.error) { + const txt = JSON.stringify({ + message: response.message, + error: response.error + }); + console.log(txt); } - // move - if (Object.keys(moveConfigs).includes(destinationStageId)) { - syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); - const moveConfig = { - ...moveConfigs[destinationStageId], - ...(await getConfig(subdomain, 'ERKHET', {})) - }; - - const postData = await getMoveData(subdomain, moveConfig, deal); + return; + } - const response = await sendRPCMessage( - models, - syncLog, - 'rpc_queue:erxes-automation-erkhet', - { - action: 'get-response-inv-movement-info', - isJson: true, - isEbarimt: false, - payload: JSON.stringify(postData), - thirdService: true - } - ); + // create sale + if (Object.keys(saleConfigs).includes(destinationStageId)) { + const brandRules = saleConfigs[destinationStageId].brandRules || {}; - if (response.message || response.error) { - const txt = JSON.stringify({ - message: response.message, - error: response.error - }); - if (moveConfig.responseField) { - await sendCardInfo(subdomain, deal, moveConfig, txt); - } else { - console.log(txt); - } - } + const brandIds = Object.keys(brandRules).filter(b => + Object.keys(mainConfigs).includes(b) + ); - return; + const configs = {}; + for (const brandId of brandIds) { + configs[brandId] = { + ...mainConfigs[brandId], + ...brandRules[brandId], + hasPayment: saleConfigs[destinationStageId].hasPayment + }; } - // create sale - if (Object.keys(configs).includes(destinationStageId)) { - syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); - const config = { - ...configs[destinationStageId], - ...(await getConfig(subdomain, 'ERKHET', {})) - }; - const postData = await getPostData(subdomain, config, deal); + const postDatas = (await getPostData( + subdomain, + models, + user, + configs, + deal + )) as any; + for (const data of postDatas) { + const { syncLog, postData } = data; const response = await sendRPCMessage( models, syncLog, @@ -164,111 +175,80 @@ export const afterMutationHandlers = async (subdomain, params) => { message: response.message, error: response.error }); - if (config.responseField) { - await sendCardInfo(subdomain, deal, config, txt); - } else { - console.log(txt); - } + console.log(txt); } - return; } return; } return; } + return; + } - if (type === 'products:product') { - syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); - if (action === 'create') { - productToErkhet(subdomain, models, syncLog, params, 'create'); - return; - } - if (action === 'update') { - productToErkhet(subdomain, models, syncLog, params, 'update'); - return; - } - if (action === 'delete') { - productToErkhet(subdomain, models, syncLog, params, 'delete'); - return; - } + if (type === 'products:product') { + if (action === 'create') { + productToErkhet(subdomain, models, params, 'create'); + return; + } + if (action === 'update') { + productToErkhet(subdomain, models, params, 'update'); + return; + } + if (action === 'delete') { + productToErkhet(subdomain, models, params, 'delete'); + return; + } + return; + } + if (type === 'products:productCategory') { + if (action === 'create') { + productCategoryToErkhet(subdomain, models, params, 'createCategory'); return; } - if (type === 'products:productCategory') { - syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); - if (action === 'create') { - productCategoryToErkhet( - subdomain, - models, - syncLog, - params, - 'createCategory' - ); - return; - } - if (action === 'update') { - productCategoryToErkhet( - subdomain, - models, - syncLog, - params, - 'updateCategory' - ); - return; - } + if (action === 'update') { + productCategoryToErkhet(subdomain, models, params, 'updateCategory'); + return; + } - if (action === 'delete') { - productCategoryToErkhet( - subdomain, - models, - syncLog, - params, - 'deleteCategory' - ); - return; - } + if (action === 'delete') { + productCategoryToErkhet(subdomain, models, params, 'deleteCategory'); + return; } + } - if (type === 'contacts:customer') { - syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); - if (action === 'create') { - customerToErkhet(subdomain, models, syncLog, params, 'create'); - return; - } + if (type === 'contacts:customer') { + if (action === 'create') { + customerToErkhet(models, params, 'create'); + return; + } - if (action === 'update') { - customerToErkhet(subdomain, models, syncLog, params, 'update'); - return; - } + if (action === 'update') { + customerToErkhet(models, params, 'update'); + return; + } - if (action === 'delete') { - customerToErkhet(subdomain, models, syncLog, params, 'delete'); - return; - } + if (action === 'delete') { + customerToErkhet(models, params, 'delete'); + return; } + } - if (type === 'contacts:company') { - syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); - if (action === 'create') { - companyToErkhet(subdomain, models, syncLog, params, 'create', user); - return; - } + if (type === 'contacts:company') { + if (action === 'create') { + companyToErkhet(models, params, 'create'); + return; + } - if (action === 'update') { - companyToErkhet(subdomain, models, syncLog, params, 'update', user); - return; - } + if (action === 'update') { + companyToErkhet(models, params, 'update'); + return; + } - if (action === 'delete') { - companyToErkhet(subdomain, models, syncLog, params, 'delete', user); - return; - } + if (action === 'delete') { + companyToErkhet(models, params, 'delete'); + return; } - } catch (e) { - await models.SyncLogs.updateOne( - { _id: syncLog._id }, - { $set: { error: e.message } } - ); } }; diff --git a/packages/plugin-syncerkhet-api/src/afterQueries.ts b/packages/plugin-syncerkhet-api/src/afterQueries.ts index b531584354..18ab2ed8dc 100644 --- a/packages/plugin-syncerkhet-api/src/afterQueries.ts +++ b/packages/plugin-syncerkhet-api/src/afterQueries.ts @@ -1,5 +1,6 @@ import { sendRequest } from '@erxes/api-utils/src/requests'; import { isEnabled } from '@erxes/api-utils/src/serviceDiscovery'; +import { generateModels } from './connectionResolver'; import { sendCoreMessage } from './messageBroker'; export default { @@ -8,6 +9,7 @@ export default { export const afterQueryHandlers = async (subdomain, data) => { const { args, results, queryName } = data; + const models = await generateModels(subdomain); if (queryName !== 'products') { return results; @@ -19,19 +21,8 @@ export const afterQueryHandlers = async (subdomain, data) => { return results; } try { - const configs = await sendCoreMessage({ - subdomain, - action: 'getConfig', - data: { code: 'ERKHET', defaultValue: {} }, - isRPC: true - }); - - const remConfigs = await sendCoreMessage({ - subdomain, - action: 'getConfig', - data: { code: 'remainderConfig', defaultValue: {} }, - isRPC: true - }); + const configs = await models.Configs.getConfig('ERKHET', {}); + const remConfigs = await models.Configs.getConfig('remainderConfig', {}); if (!Object.keys(remConfigs).includes(pipelineId)) { return results; @@ -42,7 +33,8 @@ export const afterQueryHandlers = async (subdomain, data) => { const codes = (results || []).map(item => item.code); const response = await sendRequest({ - url: configs.getRemainderApiUrl, + url: `${process.env.ERKHET_URL || + 'https://erkhet.biz'}/get-api/?kind=remainder`, method: 'GET', params: { kind: 'remainder', diff --git a/packages/plugin-syncerkhet-api/src/commands/migrateConfigs.js b/packages/plugin-syncerkhet-api/src/commands/migrateConfigs.js new file mode 100644 index 0000000000..26bdae48df --- /dev/null +++ b/packages/plugin-syncerkhet-api/src/commands/migrateConfigs.js @@ -0,0 +1,44 @@ +const dotenv = require('dotenv'); +const { MongoClient } = require('mongodb'); + +dotenv.config(); + +const MONGO_URL = 'mongodb://localhost/erxes' + +const client = new MongoClient(MONGO_URL); +let db; + +dotenv.config(); + +const command = async () => { + console.log(`start.... ${MONGO_URL}`); + + await client.connect(); + db = client.db(); + console.log('connected...'); + + Configs = db.collection('configs'); + SyncerkhetConfigs = db.collection('syncerkhet_configs'); + + keys = ['ERKHET', 'ebarimtConfig', 'stageInMoveConfig', 'returnEbarimtConfig', 'remainderConfig'] + + for (const key of keys) { + const config = await Configs.findOne({ code: key }); + + if (config) { + await SyncerkhetConfigs.updateOne({ code: key }, { + $set: { + _id: config._id, + code: key, + value: config.value + } + }, { upsert: true }) + } + } + + console.log(`Process finished at: ${new Date()}`); + + process.exit(); +}; + +command(); diff --git a/packages/plugin-syncerkhet-api/src/connectionResolver.ts b/packages/plugin-syncerkhet-api/src/connectionResolver.ts index a3c16335bf..37642ae93e 100644 --- a/packages/plugin-syncerkhet-api/src/connectionResolver.ts +++ b/packages/plugin-syncerkhet-api/src/connectionResolver.ts @@ -3,8 +3,11 @@ import { IContext as IMainContext } from '@erxes/api-utils/src'; import { createGenerateModels } from '@erxes/api-utils/src/core'; import { ISyncLogDocument } from './models/definitions/syncLog'; import { ISyncLogModel, loadSyncLogClass } from './models/SyncLog'; +import { IConfigModel, loadConfigClass } from './models/Configs'; +import { IConfigDocument } from './models/definitions/configs'; export interface IModels { + Configs: IConfigModel; SyncLogs: ISyncLogModel; } export interface IContext extends IMainContext { @@ -17,6 +20,11 @@ export let models: IModels | null = null; export const loadClasses = (db: mongoose.Connection): IModels => { models = {} as IModels; + models.Configs = db.model( + 'syncerkhet_configs', + loadConfigClass(models) + ); + models.SyncLogs = db.model( 'syncerkhet_synclogs', loadSyncLogClass(models) diff --git a/packages/plugin-syncerkhet-api/src/graphql/resolvers/index.ts b/packages/plugin-syncerkhet-api/src/graphql/resolvers/index.ts index e85da2093b..dbfa24cab1 100644 --- a/packages/plugin-syncerkhet-api/src/graphql/resolvers/index.ts +++ b/packages/plugin-syncerkhet-api/src/graphql/resolvers/index.ts @@ -1,4 +1,6 @@ import customScalars from '@erxes/api-utils/src/customScalars'; +import configMutations from './mutations/configs'; +import configQueries from './queries/configs'; import checkSyncedMutations from './mutations/checkSynced'; import inventoryMutations from './mutations/syncInventory'; import erkhetRemainders from './queries/remainders'; @@ -9,11 +11,13 @@ const resolvers: any = async _serviceDiscovery => ({ ...customScalars, SyncHistory, Query: { + ...configQueries, ...erkhetRemainders, ...syncHistories }, Mutation: { + ...configMutations, ...checkSyncedMutations, ...inventoryMutations } diff --git a/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/checkSynced.ts b/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/checkSynced.ts index b3cdcd5bc9..dfd287e8f2 100644 --- a/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/checkSynced.ts +++ b/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/checkSynced.ts @@ -6,12 +6,8 @@ import { sendCardsMessage, sendPosMessage } from '../../../messageBroker'; import { sendRPCMessage, sendTRPCMessage } from '../../../messageBrokerErkhet'; const checkSyncedMutations = { - async toCheckSynced( - _root, - { ids }: { ids: string[] }, - { subdomain }: IContext - ) { - const config = await getConfig(subdomain, 'ERKHET', {}); + async toCheckSynced(_root, { ids }: { ids: string[] }, { models }: IContext) { + const config = await models.Configs.getConfig('ERKHET', {}); if (!config.apiToken || !config.apiKey || !config.apiSecret) { throw new Error('Erkhet config not found'); @@ -59,7 +55,7 @@ const checkSyncedMutations = { configStageId, dateType }: { dealIds: string[]; configStageId: string; dateType: string }, - { subdomain, user }: IContext + { subdomain, user, models }: IContext ) { const result: { skipped: string[]; error: string[]; success: string[] } = { skipped: [], @@ -67,11 +63,9 @@ const checkSyncedMutations = { success: [] }; - const configs = await getConfig(subdomain, 'ebarimtConfig', {}); - const moveConfigs = await getConfig(subdomain, 'stageInMoveConfig', {}); - const mainConfig = await getConfig(subdomain, 'ERKHET', {}); - - const models = await generateModels(subdomain); + const configs = await models.Configs.getConfig('ebarimtConfig', {}); + const moveConfigs = await models.Configs.getConfig('stageInMoveConfig', {}); + const mainConfig = await models.Configs.getConfig('ERKHET', {}); const deals = await sendCardsMessage({ subdomain, @@ -100,7 +94,14 @@ const checkSyncedMutations = { ...configs[syncedStageId], ...mainConfig }; - const postData = await getPostData(subdomain, config, deal, dateType); + const postData = await getPostData( + subdomain, + models, + user, + config, + deal, + dateType + ); const response = await sendRPCMessage( models, diff --git a/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/configs.ts b/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/configs.ts new file mode 100644 index 0000000000..979fc1fdcf --- /dev/null +++ b/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/configs.ts @@ -0,0 +1,70 @@ +import { IContext } from '../../../connectionResolver'; +import { checkPermission } from '@erxes/api-utils/src/permissions'; +import { putCreateLog, putUpdateLog } from '../../../logUtils'; + +const configMutations = { + /** + * Create or update config object + */ + + async syncerkhetConfigsUpdate( + _root, + { configsMap }, + { user, models, subdomain }: IContext + ) { + const codes = Object.keys(configsMap); + + for (const code of codes) { + if (!code) { + continue; + } + + const prevConfig = (await models.Configs.findOne({ code })) || { + code: '', + value: [] + }; + + const value = configsMap[code]; + const doc = { code, value }; + + await models.Configs.createOrUpdateConfig(doc); + + const updatedConfig = await models.Configs.getConfig(code); + + if (prevConfig.code) { + await putUpdateLog( + models, + subdomain, + { + type: 'config', + object: prevConfig, + newData: updatedConfig, + updatedDocument: updatedConfig, + description: updatedConfig.code + }, + user + ); + } else { + await putCreateLog( + models, + subdomain, + { + type: 'config', + description: updatedConfig.code, + object: updatedConfig, + newData: updatedConfig + }, + user + ); + } + } + } +}; + +checkPermission( + configMutations, + 'syncerkhetConfigsUpdate', + 'manageGeneralSettings' +); + +export default configMutations; diff --git a/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/syncInventory.ts b/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/syncInventory.ts index bc3c9098e2..4dff6090db 100644 --- a/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/syncInventory.ts +++ b/packages/plugin-syncerkhet-api/src/graphql/resolvers/mutations/syncInventory.ts @@ -1,5 +1,4 @@ import { IContext } from '../../../connectionResolver'; -import { getConfig } from '../../../utils/utils'; import { sendRequest } from '@erxes/api-utils/src/requests'; import { sendProductsMessage } from '../../../messageBroker'; import { @@ -8,10 +7,15 @@ import { } from '../../../utils/consumeInventory'; const inventoryMutations = { - async toCheckProducts(_root, _params, { subdomain }: IContext) { - const config = await getConfig(subdomain, 'ERKHET', {}); + async toCheckProducts( + _root, + { brandId }: { brandId: string }, + { subdomain, models }: IContext + ) { + const configs = await models.Configs.getConfig('erkhetConfig', {}); + const config = configs[brandId || 'noBrand']; - if (!config.apiToken || !config.apiKey || !config.apiSecret) { + if (!config || !config.apiToken || !config.apiKey || !config.apiSecret) { throw new Error('Erkhet config not found.'); } @@ -46,6 +50,7 @@ const inventoryMutations = { } const productCodes = products.map(p => p.code) || []; + const response = await sendRequest({ url: process.env.ERKHET_URL + '/get-api/', method: 'GET', @@ -95,6 +100,7 @@ const inventoryMutations = { (categoryOfId[product.categoryId] || {}).code ) { matchedCount = matchedCount + 1; + console.log(product.code); } else { updateProducts.push(resProd); } @@ -122,10 +128,15 @@ const inventoryMutations = { }; }, - async toCheckCategories(_root, _params, { subdomain }: IContext) { - const config = await getConfig(subdomain, 'ERKHET', {}); + async toCheckCategories( + _root, + { brandId }: { brandId: string }, + { subdomain, models }: IContext + ) { + const configs = await models.Configs.getConfig('erkhetConfig', {}); + const config = configs[brandId || 'noBrand']; - if (!config.apiToken || !config.apiKey || !config.apiSecret) { + if (!config || !config.apiToken || !config.apiKey || !config.apiSecret) { throw new Error('Erkhet config not found.'); } @@ -201,15 +212,27 @@ const inventoryMutations = { async toSyncCategories( _root, - { action, categories }: { action: string; categories: any[] }, - { subdomain }: IContext + { + brandId, + action, + categories + }: { brandId: string; action: string; categories: any[] }, + { subdomain, models }: IContext ) { + const configs = await models.Configs.getConfig('erkhetConfig', {}); + const config = configs[brandId || 'noBrand']; + + if (!config || !config.apiToken || !config.apiKey || !config.apiSecret) { + throw new Error('Erkhet config not found.'); + } + try { switch (action) { case 'CREATE': { for (const category of categories) { await consumeInventoryCategory( subdomain, + config, category, category.code, 'create' @@ -221,6 +244,7 @@ const inventoryMutations = { for (const category of categories) { await consumeInventoryCategory( subdomain, + config, category, category.code, 'update' @@ -232,6 +256,7 @@ const inventoryMutations = { for (const category of categories) { await consumeInventoryCategory( subdomain, + config, category, category.code, 'delete' @@ -252,26 +277,55 @@ const inventoryMutations = { async toSyncProducts( _root, - { action, products }: { action: string; products: any[] }, - { subdomain }: IContext + { + brandId, + action, + products + }: { brandId: string; action: string; products: any[] }, + { subdomain, models }: IContext ) { + const configs = await models.Configs.getConfig('erkhetConfig', {}); + const config = configs[brandId || 'noBrand']; + + if (!config || !config.apiToken || !config.apiKey || !config.apiSecret) { + throw new Error('Erkhet config not found.'); + } + try { switch (action) { case 'CREATE': { for (const product of products) { - await consumeInventory(subdomain, product, product.code, 'create'); + await consumeInventory( + subdomain, + config, + product, + product.code, + 'create' + ); } break; } case 'UPDATE': { for (const product of products) { - await consumeInventory(subdomain, product, product.code, 'update'); + await consumeInventory( + subdomain, + config, + product, + product.code, + 'update' + ); } break; } case 'DELETE': { for (const product of products) { - await consumeInventory(subdomain, product, product.code, 'delete'); + await consumeInventory( + subdomain, + config, + product, + product.code, + 'delete' + ); } break; } diff --git a/packages/plugin-syncerkhet-api/src/graphql/resolvers/queries/configs.ts b/packages/plugin-syncerkhet-api/src/graphql/resolvers/queries/configs.ts new file mode 100644 index 0000000000..0ad08a3bf6 --- /dev/null +++ b/packages/plugin-syncerkhet-api/src/graphql/resolvers/queries/configs.ts @@ -0,0 +1,26 @@ +import { moduleRequireLogin } from '@erxes/api-utils/src/permissions'; + +import * as dotenv from 'dotenv'; +import { IContext } from '../../../connectionResolver'; +dotenv.config(); + +const configQueries = { + /** + * Config object + */ + syncerkhetConfigs(_root, _args, { models }: IContext) { + return models.Configs.find({}); + }, + + async syncerkhetConfigsGetValue( + _root, + { code }: { code: string }, + { models }: IContext + ) { + return models.Configs.findOne({ code }).lean(); + } +}; + +moduleRequireLogin(configQueries); + +export default configQueries; diff --git a/packages/plugin-syncerkhet-api/src/graphql/resolvers/queries/remainders.ts b/packages/plugin-syncerkhet-api/src/graphql/resolvers/queries/remainders.ts index 64cd488ee4..0238770ade 100644 --- a/packages/plugin-syncerkhet-api/src/graphql/resolvers/queries/remainders.ts +++ b/packages/plugin-syncerkhet-api/src/graphql/resolvers/queries/remainders.ts @@ -1,5 +1,4 @@ import { IContext } from '../../../connectionResolver'; -import { getConfig } from '../../../utils/utils'; import { sendRequest } from '@erxes/api-utils/src/requests'; import { sendCardsMessage, @@ -13,7 +12,7 @@ const erkhetQueries = { async erkhetRemainders( _root, { productIds, stageId, pipelineId }, - { subdomain }: IContext + { subdomain, models }: IContext ) { if (!pipelineId && stageId) { const pipeline = await sendCardsMessage({ @@ -32,9 +31,9 @@ const erkhetQueries = { }[] = []; try { - const configs = await getConfig(subdomain, 'ERKHET'); + const configs = await models.Configs.getConfig('ERKHET'); - const remConfigs = await getConfig(subdomain, 'remainderConfig'); + const remConfigs = await models.Configs.getConfig('remainderConfig'); if (!Object.keys(remConfigs).includes(pipelineId)) { return []; @@ -57,7 +56,8 @@ const erkhetQueries = { const codes = (products || []).map(item => item.code); const response = await sendRequest({ - url: configs.getRemainderApiUrl, + url: `${process.env.ERKHET_URL || + 'https://erkhet.biz'}/get-api/?kind=remainder`, method: 'GET', params: { kind: 'remainder', @@ -122,18 +122,18 @@ const erkhetQueries = { endDate?: Date; isMore: boolean; }, - { subdomain }: IContext + { subdomain, models }: IContext ) { const result: any = {}; try { - const configs = await getConfig(subdomain, 'ERKHET'); + const configs = await models.Configs.getConfig('ERKHET', {}); if (!configs || !Object.keys(configs).length) { return {}; } - if (!configs.debtAccounts) { + if (configs.url || !configs.debtAccounts) { return {}; } @@ -197,7 +197,8 @@ const erkhetQueries = { } const response = await sendRequest({ - url: configs.getRemainderApiUrl, + url: `${process.env.ERKHET_URL || + 'https://erkhet.biz'}/get-api/?kind=remainder`, method: 'GET', params: sendParams, timeout: 8000 diff --git a/packages/plugin-syncerkhet-api/src/graphql/schema/configs.ts b/packages/plugin-syncerkhet-api/src/graphql/schema/configs.ts new file mode 100644 index 0000000000..013a6826df --- /dev/null +++ b/packages/plugin-syncerkhet-api/src/graphql/schema/configs.ts @@ -0,0 +1,16 @@ +export const types = ` + type Config { + _id: String! + code: String! + value: JSON + } +`; + +export const queries = ` + syncerkhetConfigs: [Config] + syncerkhetConfigsGetValue(code:String!):JSON +`; + +export const mutations = ` + syncerkhetConfigsUpdate(configsMap: JSON!): JSON +`; diff --git a/packages/plugin-syncerkhet-api/src/graphql/schema/mutations.ts b/packages/plugin-syncerkhet-api/src/graphql/schema/mutations.ts index 2868000047..6723c2047e 100644 --- a/packages/plugin-syncerkhet-api/src/graphql/schema/mutations.ts +++ b/packages/plugin-syncerkhet-api/src/graphql/schema/mutations.ts @@ -2,8 +2,8 @@ export const mutations = ` toCheckSynced(ids: [String]): [CheckResponse] toSyncDeals(dealIds: [String], configStageId: String, dateType: String): JSON toSyncOrders(orderIds: [String]): JSON - toCheckProducts(productCodes: [String]): JSON - toCheckCategories(categoryCodes: [String]): JSON - toSyncCategories(action: String, categories: [JSON]): JSON - toSyncProducts(action: String, products: [JSON]): JSON + toCheckProducts(brandId: String): JSON + toCheckCategories(brandId: String): JSON + toSyncCategories(brandId: String, action: String, categories: [JSON]): JSON + toSyncProducts(brandId: String, action: String, products: [JSON]): JSON `; diff --git a/packages/plugin-syncerkhet-api/src/graphql/typeDefs.ts b/packages/plugin-syncerkhet-api/src/graphql/typeDefs.ts index 162a75cade..0eaa6764dc 100644 --- a/packages/plugin-syncerkhet-api/src/graphql/typeDefs.ts +++ b/packages/plugin-syncerkhet-api/src/graphql/typeDefs.ts @@ -2,6 +2,11 @@ import gql from 'graphql-tag'; import { mutations } from './schema/mutations'; import { queries } from './schema/queries'; import { types } from './schema/type'; +import { + mutations as configMutations, + queries as configQueries, + types as configTypes +} from './schema/configs'; const typeDefs = async _serviceDiscovery => { return gql` @@ -20,12 +25,16 @@ const typeDefs = async _serviceDiscovery => { ) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION ${types} + ${configTypes} + extend type Mutation { ${mutations} + ${configMutations} } extend type Query { ${queries} + ${configQueries} } `; }; diff --git a/packages/plugin-syncerkhet-api/src/logUtils.ts b/packages/plugin-syncerkhet-api/src/logUtils.ts new file mode 100644 index 0000000000..233fb16e60 --- /dev/null +++ b/packages/plugin-syncerkhet-api/src/logUtils.ts @@ -0,0 +1,91 @@ +import * as _ from 'underscore'; +import { + putCreateLog as commonPutCreateLog, + putDeleteLog as commonPutDeleteLog, + putUpdateLog as commonPutUpdateLog +} from '@erxes/api-utils/src/logUtils'; + +import { IModels } from './connectionResolver'; +import { IUserDocument } from '@erxes/api-utils/src/types'; +import messageBroker from './messageBroker'; + +export type LogDesc = { + [key: string]: any; +} & { name: any }; + +/** + * @param object - Previous state of the object + * @param newData - Requested update data + * @param updatedDocument - State after any updates to the object + */ +export interface ILogDataParams { + type: string; + description?: string; + object: any; + newData?: object; + extraDesc?: object[]; + updatedDocument?: any; +} + +/** + * Prepares a create log request to log server + * @param params Log document params + * @param user User information from mutation context + */ +export const putCreateLog = async ( + models: IModels, + subdomain: string, + params: ILogDataParams, + user: IUserDocument +) => { + return commonPutCreateLog( + subdomain, + messageBroker(), + { + ...params, + type: `syncerkhet:${params.type}` + }, + user + ); +}; + +/** + * Prepares a create log request to log server + * @param params Log document params + * @param user User information from mutation context + */ +export const putUpdateLog = async ( + models: IModels, + subdomain: string, + params: ILogDataParams, + user: IUserDocument +) => { + return commonPutUpdateLog( + subdomain, + messageBroker(), + { + ...params, + type: `syncerkhet:${params.type}` + }, + user + ); +}; + +/** + * Prepares a create log request to log server + * @param params Log document params + * @param user User information from mutation context + */ +export const putDeleteLog = async ( + models: IModels, + subdomain: string, + params: ILogDataParams, + user: IUserDocument +) => { + return commonPutDeleteLog( + subdomain, + messageBroker(), + { ...params, type: `syncerkhet:${params.type}` }, + user + ); +}; diff --git a/packages/plugin-syncerkhet-api/src/messageBroker.ts b/packages/plugin-syncerkhet-api/src/messageBroker.ts index b117bb863a..5b899cae18 100644 --- a/packages/plugin-syncerkhet-api/src/messageBroker.ts +++ b/packages/plugin-syncerkhet-api/src/messageBroker.ts @@ -26,6 +26,16 @@ export const initBroker = async cl => { }; }); + consumeRPCQueue('syncerkhet:getConfig', async ({ subdomain, data }) => { + const { code, defaultValue } = data; + const models = await generateModels(subdomain); + + return { + status: 'success', + data: models.Configs.getConfig(code, defaultValue) + }; + }); + consumeRPCQueue('syncerkhet:toOrder', async ({ subdomain, data }) => { const models = await generateModels(subdomain); const { pos, order } = data; diff --git a/packages/plugin-syncerkhet-api/src/messageBrokerErkhet.ts b/packages/plugin-syncerkhet-api/src/messageBrokerErkhet.ts index 8ed9edfa21..042db11451 100644 --- a/packages/plugin-syncerkhet-api/src/messageBrokerErkhet.ts +++ b/packages/plugin-syncerkhet-api/src/messageBrokerErkhet.ts @@ -5,7 +5,7 @@ import { consumeInventoryCategory } from './utils/consumeInventory'; import redis from '@erxes/api-utils/src/redis'; -import { IModels } from './connectionResolver'; +import { generateModels, IModels } from './connectionResolver'; import { ISyncLogDocument } from './models/definitions/syncLog'; let clientErkhet; @@ -16,20 +16,42 @@ export const initBrokerErkhet = async () => { const { consumeQueue } = clientErkhet; consumeQueue('rpc_queue:erkhet', async ({ subdomain, data }) => { - const { object, old_code, action } = data; + const { object, old_code, action, api_key, api_secret } = data; + const models = await generateModels(subdomain); + + const configs = await models.Configs.getConfig('erkhetConfig', {}); + const allConfigs = (Object.values(configs) || []) as any[]; + + const config = allConfigs.find( + c => + c.apiKey === api_key && + c.apiSecret === api_secret && + c.apiToken === subdomain + ); + + if (!config) { + return; + } + const objectData = JSON.parse(object)[0]; const doc = objectData.fields; const kind = objectData.model; switch (kind) { case 'inventories.inventory': - await consumeInventory(subdomain, doc, old_code, action); + await consumeInventory(subdomain, config, doc, old_code, action); break; case 'inventories.category': - await consumeInventoryCategory(subdomain, doc, old_code, action); + await consumeInventoryCategory( + subdomain, + config, + doc, + old_code, + action + ); break; case 'customers.customer': - await consumeCustomer(subdomain, doc, old_code, action); + await consumeCustomer(subdomain, config, doc, old_code, action); break; } diff --git a/packages/plugin-syncerkhet-api/src/models/Configs.ts b/packages/plugin-syncerkhet-api/src/models/Configs.ts new file mode 100644 index 0000000000..c5b0d2fc9d --- /dev/null +++ b/packages/plugin-syncerkhet-api/src/models/Configs.ts @@ -0,0 +1,50 @@ +import { Model } from 'mongoose'; +import { configSchema, IConfig, IConfigDocument } from './definitions/configs'; +import { IModels } from '../connectionResolver'; + +export interface IConfigModel extends Model { + getConfig(code: string, defaultValue?: any): Promise; + createOrUpdateConfig({ code, value }: IConfig): IConfigDocument; +} + +export const loadConfigClass = (models: IModels) => { + class Config { + /* + * Get a Config + */ + public static async getConfig(code: string, defaultValue: any) { + const config = await models.Configs.findOne({ code }); + + if (!config || !config.code) { + return defaultValue; + } + + return config.value; + } + + /** + * Create or update config + */ + public static async createOrUpdateConfig({ + code, + value + }: { + code: string; + value: string[]; + }) { + const obj = await models.Configs.findOne({ code }); + + if (obj) { + await models.Configs.updateOne({ _id: obj._id }, { $set: { value } }); + + return models.Configs.findOne({ _id: obj._id }); + } + + return models.Configs.create({ code, value }); + } + } + + configSchema.loadClass(Config); + + return configSchema; +}; diff --git a/packages/plugin-syncerkhet-api/src/models/definitions/configs.ts b/packages/plugin-syncerkhet-api/src/models/definitions/configs.ts new file mode 100644 index 0000000000..906281b879 --- /dev/null +++ b/packages/plugin-syncerkhet-api/src/models/definitions/configs.ts @@ -0,0 +1,19 @@ +import { Document, Schema } from 'mongoose'; +import { field } from './utils'; + +export interface IConfig { + code: string; + value: any; +} + +export interface IConfigDocument extends IConfig, Document { + _id: string; +} + +// Mongoose schemas =========== + +export const configSchema = new Schema({ + _id: field({ pkey: true }), + code: field({ type: String, unique: true, label: 'Code' }), + value: field({ type: Object, label: 'Value' }) +}); diff --git a/packages/plugin-syncerkhet-api/src/utils/consumeCustomer.ts b/packages/plugin-syncerkhet-api/src/utils/consumeCustomer.ts index 622f7e28b8..99231b52cc 100644 --- a/packages/plugin-syncerkhet-api/src/utils/consumeCustomer.ts +++ b/packages/plugin-syncerkhet-api/src/utils/consumeCustomer.ts @@ -1,10 +1,13 @@ -import { getConfig } from './utils'; import { sendContactsMessage } from '../messageBroker'; import { validCompanyCode } from './customerToErkhet'; -export const consumeCustomer = async (subdomain, doc, old_code, action) => { - const config = await getConfig(subdomain, 'ERKHET', {}); - +export const consumeCustomer = async ( + subdomain, + config, + doc, + old_code, + action +) => { const isCompany = await validCompanyCode(config, doc.code); if (isCompany) { diff --git a/packages/plugin-syncerkhet-api/src/utils/consumeInventory.ts b/packages/plugin-syncerkhet-api/src/utils/consumeInventory.ts index e8623c312a..1766f7a93b 100644 --- a/packages/plugin-syncerkhet-api/src/utils/consumeInventory.ts +++ b/packages/plugin-syncerkhet-api/src/utils/consumeInventory.ts @@ -1,7 +1,12 @@ import { sendProductsMessage } from '../messageBroker'; -import { getConfig } from './utils'; -export const consumeInventory = async (subdomain, doc, old_code, action) => { +export const consumeInventory = async ( + subdomain, + config, + doc, + old_code, + action +) => { const product = await sendProductsMessage({ subdomain, action: 'findOne', @@ -10,6 +15,8 @@ export const consumeInventory = async (subdomain, doc, old_code, action) => { defaultValue: {} }); + const brandIds = product.scopeBrandIds; + if ((action === 'update' && old_code) || action === 'create') { const productCategory = await sendProductsMessage({ subdomain, @@ -18,7 +25,9 @@ export const consumeInventory = async (subdomain, doc, old_code, action) => { isRPC: true }); - const config = await getConfig(subdomain, 'ERKHET', {}); + if (!brandIds.includes(config.brandId)) { + brandIds.push(config.brandId); + } const document: any = { name: doc.nickname || doc.name, @@ -36,7 +45,8 @@ export const consumeInventory = async (subdomain, doc, old_code, action) => { description: eval('`' + config.consumeDescription + '`'), status: 'active', taxType: doc.vat_type || '', - taxCode: doc.vat_type_code || '' + taxCode: doc.vat_type_code || '', + scopeBrandIds: brandIds }; if (doc.sub_measure_unit_code && doc.ratio_measure_unit) { @@ -70,17 +80,31 @@ export const consumeInventory = async (subdomain, doc, old_code, action) => { }); } } else if (action === 'delete' && product) { - await sendProductsMessage({ - subdomain, - action: 'removeProducts', - data: { _ids: [product._id] }, - isRPC: true - }); + const anotherBrandIds = brandIds.filter(b => b && b !== config.brandId); + if (anotherBrandIds.length) { + await sendProductsMessage({ + subdomain, + action: 'updateProduct', + data: { + _id: product._id, + doc: { ...product, scopeBrandIds: anotherBrandIds } + }, + isRPC: true + }); + } else { + await sendProductsMessage({ + subdomain, + action: 'removeProducts', + data: { _ids: [product._id] }, + isRPC: true + }); + } } }; export const consumeInventoryCategory = async ( subdomain, + config, doc, old_code, action @@ -92,6 +116,8 @@ export const consumeInventoryCategory = async ( isRPC: true }); + const brandIds = productCategory.scopeBrandIds; + if ((action === 'update' && old_code) || action === 'create') { const parentCategory = await sendProductsMessage({ subdomain, @@ -100,10 +126,15 @@ export const consumeInventoryCategory = async ( isRPC: true }); + if (!brandIds.includes(config.brandId)) { + brandIds.push(config.brandId); + } + const document = { code: doc.code, name: doc.name, - order: doc.order + order: doc.order, + scopeBrandIds: brandIds }; if (productCategory) { @@ -135,13 +166,26 @@ export const consumeInventoryCategory = async ( }); } } else if (action === 'delete' && productCategory) { - await sendProductsMessage({ - subdomain, - action: 'categories.removeProductCategory', - data: { - _id: productCategory._id - }, - isRPC: true - }); + const anotherBrandIds = brandIds.filter(b => b && b !== config.brandId); + if (anotherBrandIds.length) { + await sendProductsMessage({ + subdomain, + action: 'updateProduct', + data: { + _id: productCategory._id, + doc: { ...productCategory, scopeBrandIds: anotherBrandIds } + }, + isRPC: true + }); + } else { + await sendProductsMessage({ + subdomain, + action: 'categories.removeProductCategory', + data: { + _id: productCategory._id + }, + isRPC: true + }); + } } }; diff --git a/packages/plugin-syncerkhet-api/src/utils/customerToErkhet.ts b/packages/plugin-syncerkhet-api/src/utils/customerToErkhet.ts index 55c2c63dda..194a1e4dc1 100644 --- a/packages/plugin-syncerkhet-api/src/utils/customerToErkhet.ts +++ b/packages/plugin-syncerkhet-api/src/utils/customerToErkhet.ts @@ -1,14 +1,14 @@ -import { getConfig, toErkhet } from './utils'; +import { getSyncLogDoc, toErkhet } from './utils'; import { sendRequest } from '@erxes/api-utils/src/requests'; -export const customerToErkhet = async ( - subdomain, - models, - syncLog, - params, - action -) => { - const config = await getConfig(subdomain, 'ERKHET', {}); +export const customerToErkhet = async (models, params, action) => { + const syncLogDoc = getSyncLogDoc(params); + const configs = await models.Configs.getConfig('erkhetConfig', {}); + + const configBrandIds = Object.keys(configs); + if (!configBrandIds.length) { + return; + } const customer = params.updatedDocument || params.object; const oldCustomer = params.object; @@ -24,21 +24,33 @@ export const customerToErkhet = async ( name && customer.lastName ? name.concat(' - ').concat(customer.lastName || '') : name || customer.lastName || ''; - name = name ? name : config.customerDefaultName; - - sendData = { - action, - oldCode: oldCustomer.code || customer.code || '', - object: { - code: customer.code || '', - name, - defaultCategory: (config.customerCategoryCode || '').toString(), - email: customer.primaryEmail || '', - phone: customer.primaryPhone || '' - } - }; - toErkhet(models, syncLog, config, sendData, 'customer-change'); + for (const brandId of configBrandIds) { + const config = configs[brandId]; + name = name ? name : config.customerDefaultName; + + const syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); + try { + sendData = { + action, + oldCode: oldCustomer.code || customer.code || '', + object: { + code: customer.code || '', + name, + defaultCategory: (config.customerCategoryCode || '').toString(), + email: customer.primaryEmail || '', + phone: customer.primaryPhone || '' + } + }; + + toErkhet(models, syncLog, config, sendData, 'customer-change'); + } catch (e) { + await models.SyncLogs.updateOne( + { _id: syncLog._id }, + { $set: { error: e.message } } + ); + } + } }; export const validCompanyCode = async (config, companyCode) => { @@ -67,30 +79,42 @@ export const validCompanyCode = async (config, companyCode) => { return result; }; -export const companyToErkhet = async ( - subdomain, - models, - syncLog, - params, - action, - user -) => { - const config = await getConfig(subdomain, 'ERKHET', {}); +export const companyToErkhet = async (models, params, action) => { + const syncLogDoc = getSyncLogDoc(params); + const configs = await models.Configs.getConfig('erkhetConfig', {}); + + const configBrandIds = Object.keys(configs); + if (!configBrandIds.length) { + return; + } + const company = params.updatedDocument || params.object; const oldCompany = params.object; - const sendData = { - action, - oldCode: oldCompany.code || company.code || '', - object: { - code: company.code || '', - name: company.primaryName, - defaultCategory: config.companyCategoryCode, - email: company.primaryEmail || '', - phone: company.primaryPhone || '' - } - }; + for (const brandId of configBrandIds) { + const config = configs[brandId]; - toErkhet(models, syncLog, config, sendData, 'customer-change'); + const syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); + try { + const sendData = { + action, + oldCode: oldCompany.code || company.code || '', + object: { + code: company.code || '', + name: company.primaryName, + defaultCategory: config.companyCategoryCode, + email: company.primaryEmail || '', + phone: company.primaryPhone || '' + } + }; + + toErkhet(models, syncLog, config, sendData, 'customer-change'); + } catch (e) { + await models.SyncLogs.updateOne( + { _id: syncLog._id }, + { $set: { error: e.message } } + ); + } + } }; diff --git a/packages/plugin-syncerkhet-api/src/utils/ebarimtData.ts b/packages/plugin-syncerkhet-api/src/utils/ebarimtData.ts index d509e80c2d..173dfc1955 100644 --- a/packages/plugin-syncerkhet-api/src/utils/ebarimtData.ts +++ b/packages/plugin-syncerkhet-api/src/utils/ebarimtData.ts @@ -1,9 +1,9 @@ -import { sendRequest } from '@erxes/api-utils/src/requests'; import { sendContactsMessage, sendCoreMessage, sendProductsMessage } from '../messageBroker'; +import { getSyncLogDoc } from './utils'; export const validConfigMsg = async config => { if (!config.url) { @@ -12,10 +12,19 @@ export const validConfigMsg = async config => { return ''; }; -export const getPostData = async (subdomain, config, deal, dateType = '') => { +export const getPostData = async ( + subdomain, + models, + user, + configs, + deal, + dateType = '' +) => { let billType = 1; let customerCode = ''; + const syncLogDoc = getSyncLogDoc({ type: 'cards:deal', user, object: deal }); + const companyIds = await sendCoreMessage({ subdomain, action: 'conformities.savedConformity', @@ -36,22 +45,7 @@ export const getPostData = async (subdomain, config, deal, dateType = '') => { defaultValue: [] }); - const re = new RegExp('(^[А-ЯЁӨҮ]{2}[0-9]{8}$)|(^\\d{7}$)', 'gui'); - for (const company of companies) { - if (re.test(company.code)) { - const checkCompanyRes = await sendRequest({ - url: config.checkCompanyUrl, - method: 'GET', - params: { regno: company.code } - }); - - if (checkCompanyRes.found) { - billType = 3; - customerCode = company.code; - continue; - } - } - } + customerCode = (companies.find(c => c.code) || {}).code || ''; } if (billType === 1) { @@ -115,13 +109,11 @@ export const getPostData = async (subdomain, config, deal, dateType = '') => { defaultValue: [] }); - const productCodeById = {}; + const productById = {}; for (const product of products) { - productCodeById[product._id] = product.code; + productById[product._id] = product; } - const details: any = []; - const branchIds = deal.productsData.map(pd => pd.branchId) || []; const departmentIds = deal.productsData.map(pd => pd.departmentId) || []; @@ -156,6 +148,11 @@ export const getPostData = async (subdomain, config, deal, dateType = '') => { } } + const configBrandIds = Object.keys(configs); + const detailByBrandId: any = {}; + + const details: any = []; + for (const productData of deal.productsData) { // not tickUsed product not sent if (!productData.tickUsed) { @@ -163,10 +160,12 @@ export const getPostData = async (subdomain, config, deal, dateType = '') => { } // if wrong productId then not sent - if (!productCodeById[productData.productId]) { + if (!productById[productData.productId]) { continue; } + const product = productById[productData.productId]; + let otherCode: string = ''; if (productData.branchId || productData.departmentId) { @@ -175,54 +174,41 @@ export const getPostData = async (subdomain, config, deal, dateType = '') => { otherCode = `${branch.code || ''}_${department.code || ''}`; } - details.push({ - count: productData.quantity, - amount: productData.amount, - discount: productData.discount, - inventoryCode: productCodeById[productData.productId], - otherCode, - workerEmail: - productData.assignUserId && userEmailById[productData.assignUserId] - }); - } - - // debit payments coll - const payments = {}; - const configure = { - prepay: 'preAmount', - cash: 'cashAmount', - bank: 'mobileAmount', - pos: 'cardAmount', - wallet: 'debtAmount', - barter: 'debtBarterAmount', - after: 'debtAmount', - other: 'debtAmount' - }; - - let sumSaleAmount = details.reduce((predet, detail) => { - return { amount: predet.amount + detail.amount }; - }).amount; - - for (const paymentKind of Object.keys(deal.paymentsData || [])) { - const payment = deal.paymentsData[paymentKind]; - payments[configure[paymentKind]] = - (payments[configure[paymentKind]] || 0) + payment.amount; - sumSaleAmount = sumSaleAmount - payment.amount; - } + if ( + !(product.scopeBrandIds || []).length && + configBrandIds.includes('noBrand') + ) { + if (!detailByBrandId['noBrand']) { + detailByBrandId['noBrand'] = []; + } + detailByBrandId['noBrand'].push({ + count: productData.quantity, + amount: productData.amount, + discount: productData.discount, + inventoryCode: product.code, + otherCode, + workerEmail: + productData.assignUserId && userEmailById[productData.assignUserId] + }); + continue; + } - // if payments is less sum sale amount then create debt - if (sumSaleAmount > 0.005) { - payments[config.defaultPay] = - (payments[config.defaultPay] || 0) + sumSaleAmount; - } else if (sumSaleAmount < -0.005) { - if ((payments[config.defaultPay] || 0) > 0.005) { - payments[config.defaultPay] = payments[config.defaultPay] + sumSaleAmount; - } else { - for (const key of Object.keys(payments)) { - if (payments[key] > 0.005) { - payments[key] = payments[key] + sumSaleAmount; - continue; + for (const brandId of configBrandIds) { + if (product.scopeBrandIds.includes(brandId)) { + if (!detailByBrandId[brandId]) { + detailByBrandId[brandId] = []; } + + detailByBrandId[brandId].push({ + count: productData.quantity, + amount: productData.amount, + discount: productData.discount, + inventoryCode: product.code, + otherCode, + workerEmail: + productData.assignUserId && userEmailById[productData.assignUserId] + }); + continue; } } } @@ -257,33 +243,94 @@ export const getPostData = async (subdomain, config, deal, dateType = '') => { break; } - const orderInfos = [ - { - date, - checkDate, - orderId: deal._id, - number: deal.number || '', - hasVat: config.hasVat || false, - hasCitytax: config.hasCitytax || false, - billType, - customerCode, - description: deal.name, - workerEmail: - (deal.assignedUserIds.length && - userEmailById[deal.assignedUserIds[0]]) || - undefined, - details, - ...payments + // debit payments coll + const configure = { + prepay: 'preAmount', + cash: 'cashAmount', + bank: 'mobileAmount', + pos: 'cardAmount', + wallet: 'debtAmount', + barter: 'debtBarterAmount', + after: 'debtAmount', + other: 'debtAmount' + }; + + const postDatas: any[] = []; + for (const brandId of Object.keys(detailByBrandId)) { + const details = detailByBrandId[brandId]; + if (!details || !details.length) { + continue; } - ]; - return { - userEmail: config.userEmail, - token: config.apiToken, - apiKey: config.apiKey, - apiSecret: config.apiSecret, - orderInfos: JSON.stringify(orderInfos) - }; + const config = configs[brandId]; + const payments = {}; + + if (config.hasPayment) { + let sumSaleAmount = details.reduce((predet, detail) => { + return { amount: predet.amount + detail.amount }; + }).amount; + + for (const paymentKind of Object.keys(deal.paymentsData || [])) { + const payment = deal.paymentsData[paymentKind]; + payments[configure[paymentKind]] = + (payments[configure[paymentKind]] || 0) + payment.amount; + sumSaleAmount = sumSaleAmount - payment.amount; + } + + // if payments is less sum sale amount then create debt + if (sumSaleAmount > 0.005) { + payments[config.defaultPay] = + (payments[config.defaultPay] || 0) + sumSaleAmount; + } else if (sumSaleAmount < -0.005) { + if ((payments[config.defaultPay] || 0) > 0.005) { + payments[config.defaultPay] = + payments[config.defaultPay] + sumSaleAmount; + } else { + for (const key of Object.keys(payments)) { + if (payments[key] > 0.005) { + payments[key] = payments[key] + sumSaleAmount; + continue; + } + } + } + } + } + + const orderInfos = [ + { + date, + checkDate, + orderId: deal._id, + number: deal.number || '', + hasVat: config.hasVat || false, + hasCitytax: config.hasCitytax || false, + billType, + customerCode, + description: deal.name, + workerEmail: + (deal.assignedUserIds.length && + userEmailById[deal.assignedUserIds[0]]) || + undefined, + details, + ...payments + } + ]; + + const syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); + + postDatas.push({ + syncLog, + postData: { + userEmail: config.userEmail, + token: config.apiToken, + apiKey: config.apiKey, + apiSecret: config.apiSecret, + orderInfos: JSON.stringify(orderInfos) + } + }); + } + + return postDatas; }; export const getMoveData = async (subdomain, config, deal, dateType = '') => { diff --git a/packages/plugin-syncerkhet-api/src/utils/loansTransactionToErkhet.ts b/packages/plugin-syncerkhet-api/src/utils/loansTransactionToErkhet.ts index e54f383176..82f6decc86 100644 --- a/packages/plugin-syncerkhet-api/src/utils/loansTransactionToErkhet.ts +++ b/packages/plugin-syncerkhet-api/src/utils/loansTransactionToErkhet.ts @@ -1,4 +1,4 @@ -import { sendCoreMessage } from '../messageBroker'; +import { generateModels } from '../connectionResolver'; export const getPureDate = (date: Date) => { const ndate = new Date(date); @@ -6,21 +6,13 @@ export const getPureDate = (date: Date) => { return new Date(ndate.getTime() - diffTimeZone); }; -export const getConfig = async (subdomain, code, defaultValue?) => { - return await sendCoreMessage({ - subdomain, - action: 'getConfig', - data: { code, defaultValue }, - isRPC: true - }); -}; - export const loansTransactionToErkhet = async ( subdomain, generals: any[] = [], orderId ) => { - let erkhetConfig = await getConfig(subdomain, 'ERKHET', {}); + const models = await generateModels(subdomain); + let erkhetConfig = await models.Configs.getConfig('ERKHET', {}); if ( !erkhetConfig || diff --git a/packages/plugin-syncerkhet-api/src/utils/orders.ts b/packages/plugin-syncerkhet-api/src/utils/orders.ts index bd7bdccd6a..9f4a2e1ba8 100644 --- a/packages/plugin-syncerkhet-api/src/utils/orders.ts +++ b/packages/plugin-syncerkhet-api/src/utils/orders.ts @@ -12,17 +12,10 @@ export const getPureDate = (date: Date) => { return new Date(ndate.getTime() - diffTimeZone); }; -export const getConfig = async (subdomain, code, defaultValue?) => { - return await sendCoreMessage({ - subdomain, - action: 'getConfig', - data: { code, defaultValue }, - isRPC: true - }); -}; - export const getPostData = async (subdomain, pos, order) => { - let erkhetConfig = await getConfig(subdomain, 'ERKHET', {}); + const models = await generateModels(subdomain); + + let erkhetConfig = await models.Configs.getConfig('ERKHET', {}); if ( !erkhetConfig || @@ -172,9 +165,10 @@ export const getPostData = async (subdomain, pos, order) => { }; export const orderDeleteToErkhet = async (subdomain, pos, order) => { - let erkhetConfig = await getConfig(subdomain, 'ERKHET', {}); const models = await generateModels(subdomain); + let erkhetConfig = await models.Configs.getConfig('ERKHET', {}); + if ( !erkhetConfig || !erkhetConfig.apiKey! || diff --git a/packages/plugin-syncerkhet-api/src/utils/productToErkhet.ts b/packages/plugin-syncerkhet-api/src/utils/productToErkhet.ts index dbf3e04325..d8231aab38 100644 --- a/packages/plugin-syncerkhet-api/src/utils/productToErkhet.ts +++ b/packages/plugin-syncerkhet-api/src/utils/productToErkhet.ts @@ -1,48 +1,168 @@ +import { IModels } from '../connectionResolver'; import { sendProductsMessage } from '../messageBroker'; -import { getConfig, toErkhet } from './utils'; +import { getSyncLogDoc, toErkhet } from './utils'; + +const productCategorySender = async ( + models, + syncLogDoc, + config, + action, + oldCategory, + category, + parentProductCategory +) => { + if (!(config.apiKey && config.apiSecret && config.apiToken)) { + return; + } + + const syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); + + try { + const sendData = { + action, + oldCode: oldCategory.code || category.code || '', + object: { + code: category.code || '', + name: category.name || '', + parentCode: parentProductCategory ? parentProductCategory.code : '' + } + }; + + toErkhet(models, syncLog, config, sendData, 'product-change'); + } catch (e) { + await models.SyncLogs.updateOne( + { _id: syncLog._id }, + { $set: { error: e.message } } + ); + } +}; export const productCategoryToErkhet = async ( subdomain, - models, - syncLog, + models: IModels, params, action ) => { - const productCategory = params.updatedDocument || params.object; - const oldProductCategory = params.object; + const syncLogDoc = getSyncLogDoc(params); + const category = params.updatedDocument || params.object; + const oldCategory = params.object; + + const configs = await models.Configs.getConfig('erkhetConfig', {}); + const configBrandIds = Object.keys(configs); + if (!configBrandIds.length) { + return; + } const parentProductCategory = await sendProductsMessage({ subdomain, action: 'categories.findOne', - data: { _id: productCategory.parentId }, + data: { _id: category.parentId }, isRPC: true }); + const noBrandConfig = configs['noBrand']; - const config = await getConfig(subdomain, 'ERKHET', {}); + const remBrands = oldCategory.scopeBrandIds.filter( + b => !category.scopeBrandIds.includes(b) + ); + const updBrands = category.scopeBrandIds; - const sendData = { - action, - oldCode: oldProductCategory.code || productCategory.code || '', - object: { - code: productCategory.code || '', - name: productCategory.name || '', - parentCode: parentProductCategory ? parentProductCategory.code : '' - } - }; + for (const remBrand of remBrands) { + const config = configs[remBrand]; + productCategorySender( + models, + syncLogDoc, + config, + 'delete', + oldCategory, + category, + parentProductCategory + ); + } - toErkhet(models, syncLog, config, sendData, 'product-change'); + for (const updBrand of updBrands) { + const config = configs[updBrand]; + productCategorySender( + models, + syncLogDoc, + config, + action, + oldCategory, + category, + parentProductCategory + ); + } + + if (noBrandConfig && !updBrands.length) { + productCategorySender( + models, + syncLogDoc, + noBrandConfig, + action, + oldCategory, + category, + parentProductCategory + ); + } }; -export const productToErkhet = async ( - subdomain, +const productSender = async ( models, - syncLog, - params, - action + syncLogDoc, + config, + action, + oldProduct, + product, + productCategory, + subMeasureUnit, + ratioMeasureUnit ) => { + if (!(config.apiKey && config.apiSecret && config.apiToken)) { + return; + } + + const syncLog = await models.SyncLogs.syncLogsAdd(syncLogDoc); + + try { + const sendData = { + action, + oldCode: oldProduct.code || product.code || '', + object: { + code: product.code || 'ш', + name: product.name || '', + measureUnit: product.uom, + subMeasureUnit, + ratioMeasureUnit, + barcodes: product.barcodes.join(','), + unitPrice: product.unitPrice || 0, + costAccount: config.costAccount, + saleAccount: config.saleAccount, + categoryCode: productCategory ? productCategory.code : '', + defaultCategory: config.productCategoryCode, + taxType: product.taxType, + taxCode: product.taxCode + } + }; + + toErkhet(models, syncLog, config, sendData, 'product-change'); + } catch (e) { + await models.SyncLogs.updateOne( + { _id: syncLog._id }, + { $set: { error: e.message } } + ); + } +}; + +export const productToErkhet = async (subdomain, models, params, action) => { + const syncLogDoc = getSyncLogDoc(params); const product = params.updatedDocument || params.object; const oldProduct = params.object; + const configs = await models.Configs.getConfig('erkhetConfig', {}); + const configBrandIds = Object.keys(configs); + if (!configBrandIds.length) { + return; + } + const productCategory = await sendProductsMessage({ subdomain, action: 'categories.findOne', @@ -59,27 +179,54 @@ export const productToErkhet = async ( ratioMeasureUnit = subUom.ratio; } - const config = await getConfig(subdomain, 'ERKHET', {}); + const noBrandConfig = configs['noBrand']; + + const remBrands = oldProduct.scopeBrandIds.filter( + b => !product.scopeBrandIds.includes(b) + ); + const updBrands = product.scopeBrandIds; + + for (const remBrand of remBrands) { + const config = configs[remBrand]; + productSender( + models, + syncLogDoc, + config, + 'delete', + oldProduct, + product, + productCategory, + subMeasureUnit, + ratioMeasureUnit + ); + } - const sendData = { - action, - oldCode: oldProduct.code || product.code || '', - object: { - code: product.code || 'ш', - name: product.name || '', - measureUnit: product.uom, + for (const updBrand of updBrands) { + const config = configs[updBrand]; + productSender( + models, + syncLogDoc, + config, + action, + oldProduct, + product, + productCategory, subMeasureUnit, - ratioMeasureUnit, - barcodes: product.barcodes.join(','), - unitPrice: product.unitPrice || 0, - costAccount: config.costAccount, - saleAccount: config.saleAccount, - categoryCode: productCategory ? productCategory.code : '', - defaultCategory: config.productCategoryCode, - taxType: product.taxType, - taxCode: product.taxCode - } - }; - - toErkhet(models, syncLog, config, sendData, 'product-change'); + ratioMeasureUnit + ); + } + + if (noBrandConfig && !updBrands.length) { + productSender( + models, + syncLogDoc, + noBrandConfig, + action, + oldProduct, + product, + productCategory, + subMeasureUnit, + ratioMeasureUnit + ); + } }; diff --git a/packages/plugin-syncerkhet-api/src/utils/utils.ts b/packages/plugin-syncerkhet-api/src/utils/utils.ts index 0e4bd2afbd..777f91c485 100644 --- a/packages/plugin-syncerkhet-api/src/utils/utils.ts +++ b/packages/plugin-syncerkhet-api/src/utils/utils.ts @@ -1,6 +1,28 @@ -import { sendCardsMessage, sendCoreMessage } from '../messageBroker'; +import { IUserDocument } from '@erxes/api-utils/src/types'; +import { IModels } from '../connectionResolver'; +import { sendCardsMessage } from '../messageBroker'; import { sendRPCMessage } from '../messageBrokerErkhet'; +export const getSyncLogDoc = (params: { + type: string; + user: IUserDocument; + object: any; + brandId?: string; +}) => { + const { type, user, brandId } = params; + + return { + type: '', + brandId, + contentType: type, + contentId: params.object._id, + createdAt: new Date(), + createdBy: user._id, + consumeData: params, + consumeStr: JSON.stringify(params) + }; +}; + export const toErkhet = (models, syncLog, config, sendData, action) => { const postData = { token: config.apiToken, @@ -16,13 +38,8 @@ export const toErkhet = (models, syncLog, config, sendData, action) => { }); }; -export const getConfig = async (subdomain, code, defaultValue?) => { - return await sendCoreMessage({ - subdomain, - action: 'getConfig', - data: { code, defaultValue }, - isRPC: true - }); +export const getConfig = async (models: IModels, code, defaultValue?) => { + return models.Configs.getConfig(code, defaultValue); }; export const sendCardInfo = async (subdomain, deal, config, value) => { diff --git a/packages/plugin-syncerkhet-ui/src/components/GeneralPerSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/GeneralPerSettings.tsx new file mode 100644 index 0000000000..8667d99c0f --- /dev/null +++ b/packages/plugin-syncerkhet-ui/src/components/GeneralPerSettings.tsx @@ -0,0 +1,209 @@ +import { + Button, + CollapseContent, + ControlLabel, + FormControl, + FormGroup +} from '@erxes/ui/src/components'; +import SelectBrands from '@erxes/ui/src/brands/containers/SelectBrands'; +import { __ } from '@erxes/ui/src/utils'; +import React from 'react'; +import { KEY_LABELS } from '../constants'; +import { ContentBox } from '../styles'; +import { IConfigsMap } from '../types'; +import { isEnabled } from '@erxes/ui/src/utils/core'; +import { + FormColumn, + FormWrapper, + ModalFooter +} from '@erxes/ui/src/styles/main'; + +type Props = { + configsMap: IConfigsMap; + config: any; + currentConfigKey: string; + save: (configsMap: IConfigsMap) => void; + delete: (currentConfigKey: string) => void; +}; + +type State = { + config: any; + hasOpen: boolean; +}; + +class PerSettings extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { + config: props.config, + hasOpen: false + }; + } + + onSave = e => { + e.preventDefault(); + const { configsMap, currentConfigKey } = this.props; + + const { config } = this.state; + const key = config.brandId; + + delete configsMap.erkhetConfig[currentConfigKey]; + configsMap.erkhetConfig[key] = config; + this.props.save(configsMap); + }; + + onDelete = e => { + e.preventDefault(); + + this.props.delete(this.props.currentConfigKey); + }; + + onChangeConfig = (code: string, value) => { + const { config } = this.state; + + config[code] = value; + + this.setState({ config }); + }; + + onChangeInput = (code: string, e) => { + this.onChangeConfig(code, e.target.value); + }; + + onChangeBrand = (brandId: string) => { + this.setState({ config: { ...this.state.config, brandId } }); + }; + + renderItem = (key: string, description?: string) => { + const { config } = this.state; + + return ( + + {KEY_LABELS[key]} + {description &&

{__(description)}

} + +
+ ); + }; + + render() { + const { config } = this.state; + + return ( + + + + {this.renderItem('title')} + + + + Brand + this.onChangeBrand(brand as string)} + initialValue={config.brandId} + multi={false} + name="selectedBrands" + customOption={{ + label: 'No Brand (noBrand)', + value: 'noBrand' + }} + /> + + {this.renderItem('apiToken')} + + + {this.renderItem('apiKey')} + {this.renderItem('apiSecret')} + + + + + + + {this.renderItem( + 'costAccount', + 'Cost Account fullCode on erkhet' + )} + {this.renderItem( + 'saleAccount', + 'Sale Account fullCode on erkhet' + )} + + + {this.renderItem( + 'productCategoryCode', + 'Default Category Code on erkhet inventory' + )} + {this.renderItem( + 'consumeDescription', + 'Set description when incoming erkhet inventory' + )} + + + + + + + {this.renderItem('checkCompanyUrl')} + {this.renderItem( + 'customerDefaultName', + 'Customer default name on erkhet' + )} + {this.renderItem('debtAccounts', 'Split "," account fullcode')} + + + {this.renderItem( + 'customerCategoryCode', + 'Customer default category code on erkhet' + )} + {this.renderItem( + 'companyCategoryCode', + 'Company default category code on erkhet' + )} + + + + {isEnabled('loans') && ( + + {this.renderItem('userEmail', 'user email')} + {this.renderItem( + 'defaultCustomer', + 'Customer default code on erkhet' + )} + + )} + + + + + + + + ); + } +} + +export default PerSettings; diff --git a/packages/plugin-syncerkhet-ui/src/components/GeneralSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/GeneralSettings.tsx index ef4b411b1a..f4631ed160 100644 --- a/packages/plugin-syncerkhet-ui/src/components/GeneralSettings.tsx +++ b/packages/plugin-syncerkhet-ui/src/components/GeneralSettings.tsx @@ -1,20 +1,14 @@ -import { - Button, - CollapseContent, - ControlLabel, - FormControl, - FormGroup -} from '@erxes/ui/src/components'; import { MainStyleTitle as Title } from '@erxes/ui/src/styles/eindex'; import { __ } from '@erxes/ui/src/utils'; +import { Button } from '@erxes/ui/src/components'; import { Wrapper } from '@erxes/ui/src/layout'; import React from 'react'; -import { KEY_LABELS } from '../constants'; + import { ContentBox } from '../styles'; import { IConfigsMap } from '../types'; import Header from './Header'; +import PerSettings from './GeneralPerSettings'; import Sidebar from './Sidebar'; -import { isEnabled } from '@erxes/ui/src/utils/core'; type Props = { save: (configsMap: IConfigsMap) => void; @@ -22,7 +16,7 @@ type Props = { }; type State = { - currentMap: IConfigsMap; + configsMap: IConfigsMap; }; class GeneralSettings extends React.Component { @@ -30,134 +24,109 @@ class GeneralSettings extends React.Component { super(props); this.state = { - currentMap: props.configsMap.ERKHET || {} + configsMap: props.configsMap }; } - save = e => { + add = e => { e.preventDefault(); + const { configsMap } = this.state; + + if (!configsMap.erkhetConfig) { + configsMap.erkhetConfig = {}; + } + + // must save prev item saved then new item + configsMap.erkhetConfig.newBrandId = { + title: 'New Erkhet Config', + brandId: '', + apiKey: '', + apiSecret: '', + apiToken: '', + costAccount: '', + saleAccount: '', + productCategoryCode: '', + consumeDescription: '', + customerDefaultName: '', + customerCategoryCode: '', + companyCategoryCode: '', + debtAccounts: '', + userEmail: '', + defaultCustomer: '' + }; - const { currentMap } = this.state; - const { configsMap } = this.props; - configsMap.ERKHET = currentMap; - this.props.save(configsMap); + this.setState({ configsMap }); }; - onChangeConfig = (code: string, value) => { - const { currentMap } = this.state; + delete = (currentConfigKey: string) => { + const { configsMap } = this.state; + delete configsMap.erkhetConfig[currentConfigKey]; + delete configsMap.erkhetConfig['newBrandId']; - currentMap[code] = value; + this.setState({ configsMap }); - this.setState({ currentMap }); + this.props.save(configsMap); }; - onChangeInput = (code: string, e) => { - this.onChangeConfig(code, e.target.value); - }; + renderConfigs(configs) { + return Object.keys(configs).map(key => { + return ( + + ); + }); + } - renderItem = (key: string, description?: string) => { - const { currentMap } = this.state; + renderContent() { + const { configsMap } = this.state; + const configs = configsMap.erkhetConfig || {}; return ( - - {KEY_LABELS[key]} - {description &&

{__(description)}

} - -
+ + {this.renderConfigs(configs)} + ); - }; + } render() { const breadcrumb = [ { title: __('Settings'), link: '/settings' }, - { title: __('Sync erkhet config') } + { title: __('Erkhet config') } ]; const actionButtons = ( ); - const content = ( - - - {this.renderItem('apiKey')} - {this.renderItem('apiSecret')} - {this.renderItem('apiToken')} - {this.renderItem( - 'getRemainderApiUrl', - 'Get remainder from erkhet api url' - )} - - - {this.renderItem('costAccount', 'Cost Account fullCode on erkhet')} - {this.renderItem('saleAccount', 'Sale Account fullCode on erkhet')} - {this.renderItem( - 'productCategoryCode', - 'Default Category Code on erkhet inventory' - )} - {this.renderItem( - 'consumeDescription', - 'Set description when incoming erkhet inventory' - )} - - - {this.renderItem('checkCompanyUrl')} - {this.renderItem( - 'customerDefaultName', - 'Customer default name on erkhet' - )} - {this.renderItem( - 'customerCategoryCode', - 'Customer default category code on erkhet' - )} - {this.renderItem( - 'companyCategoryCode', - 'Company default category code on erkhet' - )} - {this.renderItem('debtAccounts', 'Split "," account fullcode')} - - {isEnabled('loans') && ( - - {this.renderItem('userEmail', 'user email')} - {this.renderItem( - 'defaultCustomer', - 'Customer default code on erkhet' - )} - - )} - - ); - return ( + } mainHead={
} actionBar={ {__('Sync erkhet configs')}} + left={{__('Erkhet configs')}} right={actionButtons} - background="colorWhite" /> } leftSidebar={} - content={content} - transparent={true} + content={this.renderContent()} hasBorder={true} + transparent={true} /> ); } diff --git a/packages/plugin-syncerkhet-ui/src/components/PerMoveSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/MovePerSettings.tsx similarity index 100% rename from packages/plugin-syncerkhet-ui/src/components/PerMoveSettings.tsx rename to packages/plugin-syncerkhet-ui/src/components/MovePerSettings.tsx diff --git a/packages/plugin-syncerkhet-ui/src/components/StageMoveSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/MoveStageSettings.tsx similarity index 98% rename from packages/plugin-syncerkhet-ui/src/components/StageMoveSettings.tsx rename to packages/plugin-syncerkhet-ui/src/components/MoveStageSettings.tsx index a047d1d548..90e8ec3898 100755 --- a/packages/plugin-syncerkhet-ui/src/components/StageMoveSettings.tsx +++ b/packages/plugin-syncerkhet-ui/src/components/MoveStageSettings.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { ContentBox } from '../styles'; import { IConfigsMap } from '../types'; import Header from './Header'; -import PerSettings from './PerMoveSettings'; +import PerSettings from './MovePerSettings'; import Sidebar from './Sidebar'; type Props = { diff --git a/packages/plugin-syncerkhet-ui/src/components/PerRemSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/RemPerSettings.tsx similarity index 100% rename from packages/plugin-syncerkhet-ui/src/components/PerRemSettings.tsx rename to packages/plugin-syncerkhet-ui/src/components/RemPerSettings.tsx diff --git a/packages/plugin-syncerkhet-ui/src/components/PipelineSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/RemPipelineSettings.tsx similarity index 98% rename from packages/plugin-syncerkhet-ui/src/components/PipelineSettings.tsx rename to packages/plugin-syncerkhet-ui/src/components/RemPipelineSettings.tsx index 23c01fcc55..2f7c2322f0 100755 --- a/packages/plugin-syncerkhet-ui/src/components/PipelineSettings.tsx +++ b/packages/plugin-syncerkhet-ui/src/components/RemPipelineSettings.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { ContentBox } from '../styles'; import { IConfigsMap } from '../types'; import Header from './Header'; -import PerRemSettings from './PerRemSettings'; +import PerRemSettings from './RemPerSettings'; import Sidebar from './Sidebar'; type Props = { diff --git a/packages/plugin-syncerkhet-ui/src/components/ReturnPerSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/ReturnPerSettings.tsx index ff5acda569..49d3955db2 100644 --- a/packages/plugin-syncerkhet-ui/src/components/ReturnPerSettings.tsx +++ b/packages/plugin-syncerkhet-ui/src/components/ReturnPerSettings.tsx @@ -52,8 +52,8 @@ class PerSettings extends React.Component { const { config } = this.state; const key = config.stageId; - delete configsMap.returnEbarimtConfig[currentConfigKey]; - configsMap.returnEbarimtConfig[key] = config; + delete configsMap.stageInReturnConfig[currentConfigKey]; + configsMap.stageInReturnConfig[key] = config; this.props.save(configsMap); }; @@ -118,7 +118,11 @@ class PerSettings extends React.Component { return ( {'Title'} diff --git a/packages/plugin-syncerkhet-ui/src/components/ReturnStageSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/ReturnStageSettings.tsx index 1d47291e71..030d67e03e 100755 --- a/packages/plugin-syncerkhet-ui/src/components/ReturnStageSettings.tsx +++ b/packages/plugin-syncerkhet-ui/src/components/ReturnStageSettings.tsx @@ -32,12 +32,12 @@ class GeneralSettings extends React.Component { e.preventDefault(); const { configsMap } = this.state; - if (!configsMap.returnEbarimtConfig) { - configsMap.returnEbarimtConfig = {}; + if (!configsMap.stageInReturnConfig) { + configsMap.stageInReturnConfig = {}; } // must save prev item saved then new item - configsMap.returnEbarimtConfig.newEbarimtConfig = { + configsMap.stageInReturnConfig.newStageInReturnConfig = { title: 'New Erkhet Config', boardId: '', pipelineId: '', @@ -53,8 +53,8 @@ class GeneralSettings extends React.Component { delete = (currentConfigKey: string) => { const { configsMap } = this.state; - delete configsMap.returnEbarimtConfig[currentConfigKey]; - delete configsMap.returnEbarimtConfig['newEbarimtConfig']; + delete configsMap.stageInReturnConfig[currentConfigKey]; + delete configsMap.stageInReturnConfig['newStageInReturnConfig']; this.setState({ configsMap }); @@ -77,7 +77,7 @@ class GeneralSettings extends React.Component { renderContent() { const { configsMap } = this.state; - const configs = configsMap.returnEbarimtConfig || {}; + const configs = configsMap.stageInReturnConfig || {}; return ( diff --git a/packages/plugin-syncerkhet-ui/src/components/PerSettings.tsx b/packages/plugin-syncerkhet-ui/src/components/SalePerSettings.tsx similarity index 52% rename from packages/plugin-syncerkhet-ui/src/components/PerSettings.tsx rename to packages/plugin-syncerkhet-ui/src/components/SalePerSettings.tsx index b0695d1ad8..464590c8b5 100644 --- a/packages/plugin-syncerkhet-ui/src/components/PerSettings.tsx +++ b/packages/plugin-syncerkhet-ui/src/components/SalePerSettings.tsx @@ -3,7 +3,8 @@ import { CollapseContent, ControlLabel, FormControl, - FormGroup + FormGroup, + Tip } from '@erxes/ui/src/components'; import client from '@erxes/ui/src/apolloClient'; import { gql } from '@apollo/client'; @@ -13,10 +14,12 @@ import { MainStyleModalFooter as ModalFooter } from '@erxes/ui/src/styles/eindex import Select from 'react-select-plus'; import React from 'react'; import { IConfigsMap } from '../types'; -import { FieldsCombinedByType } from '../../../ui-forms/src/settings/properties/types'; +import { FieldsCombinedByType } from '@erxes/ui-forms/src/settings/properties/types'; import { isEnabled } from '@erxes/ui/src/utils/core'; import { queries as formQueries } from '@erxes/ui-forms/src/forms/graphql'; import { FormColumn, FormWrapper } from '@erxes/ui/src/styles/main'; +import SelectBrands from '@erxes/ui/src/brands/containers/SelectBrands'; +import { GroupWrapper } from '@erxes/ui-segments/src/styles'; type Props = { configsMap: IConfigsMap; @@ -29,6 +32,7 @@ type Props = { type State = { config: any; hasOpen: boolean; + brandRules: any; fieldsCombined: FieldsCombinedByType[]; }; @@ -39,6 +43,7 @@ class PerSettings extends React.Component { this.state = { config: props.config, hasOpen: false, + brandRules: props.config.brandRules || {}, fieldsCombined: [] }; @@ -73,11 +78,11 @@ class PerSettings extends React.Component { onSave = e => { e.preventDefault(); const { configsMap, currentConfigKey } = this.props; - const { config } = this.state; + const { config, brandRules } = this.state; const key = config.stageId; - delete configsMap.ebarimtConfig[currentConfigKey]; - configsMap.ebarimtConfig[key] = config; + delete configsMap.stageInSaleConfig[currentConfigKey]; + configsMap.stageInSaleConfig[key] = { ...config, brandRules }; this.props.save(configsMap); }; @@ -134,32 +139,161 @@ class PerSettings extends React.Component { {title || key} {description &&

{__(description)}

}
); }; + addConfig = () => { + const { brandRules } = this.state; + this.setState({ + brandRules: { + ...brandRules, + newBrand: { + brandId: '', + userEmail: '', + hasPayment: true, + hasVat: false, + hasCitytax: false, + defaultPay: 'debtAmount' + } + } + }); + }; + + removeConfig = brandId => { + const { brandRules } = this.state; + const newConfig = { ...brandRules }; + delete newConfig[brandId]; + this.setState({ + brandRules: newConfig + }); + }; + + updateConfig = (brandId, key, value) => { + const { brandRules } = this.state; + + if (key === 'brandId') { + delete brandRules.newBrand; + } + brandRules[brandId] = { ...brandRules[brandId], [key]: value }; + this.setState({ + brandRules: brandRules + }); + }; + + renderPerConfig() { + const { brandRules } = this.state; + + return Object.keys(brandRules).map(key => { + return ( + + + Brand + this.updateConfig(brand, 'brandId', brand)} + multi={false} + /> + + + + + User Email + + this.updateConfig(key, 'userEmail', (e.target as any).value) + } + required={true} + /> + + + Has Vat + + this.updateConfig(key, 'hasVat', (e.target as any).checked) + } + /> + + + + + default Pay + { /> - - {this.renderInput('userEmail', 'userEmail', '')} - {this.renderCheckbox('hasVat', 'hasVat', '')} - {this.renderCheckbox('hasCitytax', 'hasCitytax', '')} - - - {'defaultPay'} -