diff --git a/packages/apps/purchase/src/api/purchase-request-line-api.js b/packages/apps/purchase/src/api/purchase-request-line-api.js index 19079848b..3b7bd0fcc 100644 --- a/packages/apps/purchase/src/api/purchase-request-line-api.js +++ b/packages/apps/purchase/src/api/purchase-request-line-api.js @@ -16,22 +16,49 @@ * along with this program. If not, see . */ -import {createStandardSearch} from '@axelor/aos-mobile-core'; +import { + createStandardSearch, + getSearchCriterias, +} from '@axelor/aos-mobile-core'; -const createPurchaseRequestLineCriteria = purchaseRequestId => { - return [ +const createPurchaseRequestLineCriteria = ({ + searchValue, + purchaseRequestId, + newProduct, +}) => { + const criterias = [ + getSearchCriterias('purchase_purchaseRequestLine', searchValue), { fieldName: 'purchaseRequest.id', operator: '=', value: purchaseRequestId, }, ]; + + if (newProduct != null) { + criterias.push({ + fieldName: 'newProduct', + operator: '=', + value: newProduct, + }); + } + + return criterias; }; -export async function searchPurchaseRequestLine({page = 0, purchaseRequestId}) { +export async function searchPurchaseRequestLine({ + page = 0, + searchValue, + purchaseRequestId, + newProduct, +}) { return createStandardSearch({ model: 'com.axelor.apps.purchase.db.PurchaseRequestLine', - criteria: createPurchaseRequestLineCriteria(purchaseRequestId), + criteria: createPurchaseRequestLineCriteria({ + searchValue, + purchaseRequestId, + newProduct, + }), fieldKey: 'purchase_purchaseRequestLine', sortKey: 'purchase_purchaseRequestLine', page, diff --git a/packages/apps/purchase/src/components/atoms/RequestLineCard/RequestLineCard.tsx b/packages/apps/purchase/src/components/atoms/RequestLineCard/RequestLineCard.tsx new file mode 100644 index 000000000..f28fa6eba --- /dev/null +++ b/packages/apps/purchase/src/components/atoms/RequestLineCard/RequestLineCard.tsx @@ -0,0 +1,78 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2024 Axelor (). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, {useMemo} from 'react'; +import {StyleSheet} from 'react-native'; +import { + ObjectCard, + TextUnit, + usePriceFormat, + useThemeColor, +} from '@axelor/aos-mobile-ui'; + +interface RequestLineCardProps { + style?: any; + productName?: string; + unit?: string; + qty?: string; + newProduct?: boolean; +} + +const RequestLineCard = ({ + style, + productName, + unit, + qty, + newProduct, +}: RequestLineCardProps) => { + const priceFormat = usePriceFormat(); + const Colors = useThemeColor(); + + const borderStyle = useMemo(() => { + if (newProduct) { + return getStyles(Colors.plannedColor.background)?.border; + } + }, [Colors.plannedColor.background, newProduct]); + + return ( + + ), + }, + ], + }} + /> + ); +}; + +const getStyles = (color: string) => + StyleSheet.create({ + border: {borderLeftWidth: 7, borderLeftColor: color}, + }); + +export default RequestLineCard; diff --git a/packages/apps/purchase/src/components/atoms/RequestSeeLinesButton/RequestSeeLinesButton.tsx b/packages/apps/purchase/src/components/atoms/RequestSeeLinesButton/RequestSeeLinesButton.tsx index b0a30bcac..90ddd27ea 100644 --- a/packages/apps/purchase/src/components/atoms/RequestSeeLinesButton/RequestSeeLinesButton.tsx +++ b/packages/apps/purchase/src/components/atoms/RequestSeeLinesButton/RequestSeeLinesButton.tsx @@ -18,7 +18,11 @@ import React, {useMemo} from 'react'; import {StyleSheet, TouchableOpacity, View} from 'react-native'; -import {useSelector, useTranslator} from '@axelor/aos-mobile-core'; +import { + useNavigation, + useSelector, + useTranslator, +} from '@axelor/aos-mobile-core'; import { Card, Icon, @@ -30,6 +34,7 @@ import { const RequestSeeLinesButton = () => { const I18n = useTranslator(); const Colors = useThemeColor(); + const navigation = useNavigation(); const styles = useMemo( () => getStyles(Colors.secondaryColor.background), @@ -42,7 +47,7 @@ const RequestSeeLinesButton = () => { return ( {}} + onPress={() => navigation.navigate('RequestLineListScreen')} disabled={totalPurchaseRequestLine === 0} activeOpacity={0.9}> diff --git a/packages/apps/purchase/src/components/atoms/index.ts b/packages/apps/purchase/src/components/atoms/index.ts index a9335ea63..2e97a14c8 100644 --- a/packages/apps/purchase/src/components/atoms/index.ts +++ b/packages/apps/purchase/src/components/atoms/index.ts @@ -17,6 +17,7 @@ */ export {default as DropdownRequestCharacteristics} from './DropdownRequestCharacteristics/DropdownRequestCharacteristics'; -export {default as RequestSeeLinesButton} from './RequestSeeLinesButton/RequestSeeLinesButton'; export {default as RequestCard} from './RequestCard/RequestCard'; export {default as RequestHeader} from './RequestHeader/RequestHeader'; +export {default as RequestLineCard} from './RequestLineCard/RequestLineCard'; +export {default as RequestSeeLinesButton} from './RequestSeeLinesButton/RequestSeeLinesButton'; diff --git a/packages/apps/purchase/src/components/molecules/RequestDropdownCards/RequestDropdownCards.tsx b/packages/apps/purchase/src/components/molecules/RequestDropdownCards/RequestDropdownCards.tsx index f73044bde..8a952564a 100644 --- a/packages/apps/purchase/src/components/molecules/RequestDropdownCards/RequestDropdownCards.tsx +++ b/packages/apps/purchase/src/components/molecules/RequestDropdownCards/RequestDropdownCards.tsx @@ -21,7 +21,7 @@ import {useTranslator} from '@axelor/aos-mobile-core'; import {DropdownCardSwitch} from '@axelor/aos-mobile-ui'; import {DropdownRequestCharacteristics} from '../../atoms'; -const RequestDropdownCards = ({style}: {style: any}) => { +const RequestDropdownCards = ({style}: {style?: any}) => { const I18n = useTranslator(); return ( diff --git a/packages/apps/purchase/src/i18n/en.json b/packages/apps/purchase/src/i18n/en.json index f1ceeee02..316e62af9 100644 --- a/packages/apps/purchase/src/i18n/en.json +++ b/packages/apps/purchase/src/i18n/en.json @@ -1,6 +1,7 @@ { "Purchase_Purchase": "Purchase", "Purchase_InternalRequests": "Internal requests", + "Purchase_InternalRequest": "Internal request", "Purchase_Status_Draft": "Draft", "Purchase_Status_Requested": "Requested", "Purchase_Status_Accepted": "Accepted", @@ -17,6 +18,9 @@ "Purchase_Accept": "Accept", "Purchase_Refuse": "Refuse", "Purchase_NoInformationAvailable": "No information available", + "Purchase_AddProduct": "Add product", + "Purchase_NewProduct": "New product", + "Purchase_ExistingProduct": "Existing product", "Purchase_SliceAction_SearchPurchaseRequest": "search purchase request", "Purchase_SliceAction_SearchSupplier": "search supplier", "Purchase_SliceAction_GetPurchaseRequest": "get purchase request", diff --git a/packages/apps/purchase/src/i18n/fr.json b/packages/apps/purchase/src/i18n/fr.json index fbe342b34..7516def42 100644 --- a/packages/apps/purchase/src/i18n/fr.json +++ b/packages/apps/purchase/src/i18n/fr.json @@ -1,6 +1,7 @@ { "Purchase_Purchase": "Achats", "Purchase_InternalRequests": "Demandes internes", + "Purchase_InternalRequest": "Demande interne", "Purchase_Status_Draft": "Brouillon", "Purchase_Status_Requested": "Demandée", "Purchase_Status_Accepted": "Acceptée", @@ -17,6 +18,9 @@ "Purchase_Accept": "Accepter", "Purchase_Refuse": "Refuser", "Purchase_NoInformationAvailable": "Aucune information disponible", + "Purchase_AddProduct": "Ajouter un produit", + "Purchase_NewProduct": "Nouveau produit", + "Purchase_ExistingProduct": "Produit existant", "Purchase_SliceAction_SearchPurchaseRequest": "recherche sur les demandes d'achat", "Purchase_SliceAction_SearchSupplier": "recherche sur les fournisseurs", "Purchase_SliceAction_GetPurchaseRequest": "récupération de la demande d'achat", diff --git a/packages/apps/purchase/src/models/objectFields.ts b/packages/apps/purchase/src/models/objectFields.ts index efb18d96c..b7330b598 100644 --- a/packages/apps/purchase/src/models/objectFields.ts +++ b/packages/apps/purchase/src/models/objectFields.ts @@ -32,4 +32,11 @@ export const purchase_modelAPI: ObjectFields = { purchase_supplier: schemaContructor.object({ simpleFullName: schemaContructor.string(), }), + purchase_purchaseRequestLine: schemaContructor.object({ + product: schemaContructor.subObject(), + productTitle: schemaContructor.string(), + unit: schemaContructor.subObject(), + quantity: schemaContructor.string(), + newProduct: schemaContructor.boolean(), + }), }; diff --git a/packages/apps/purchase/src/models/searchFields.ts b/packages/apps/purchase/src/models/searchFields.ts index 321ca825a..0b2e9dd8a 100644 --- a/packages/apps/purchase/src/models/searchFields.ts +++ b/packages/apps/purchase/src/models/searchFields.ts @@ -21,4 +21,5 @@ import {SearchFields} from '@axelor/aos-mobile-core'; export const purchase_searchFields: SearchFields = { purchase_purchaseRequest: ['purchaseRequestSeq', 'company.name'], purchase_supplier: ['simpleFullName'], + purchase_purchaseRequestLine: ['product.fullName', 'productTitle'], }; diff --git a/packages/apps/purchase/src/models/typeObjects.ts b/packages/apps/purchase/src/models/typeObjects.ts index 01d06fb03..cf78f693a 100644 --- a/packages/apps/purchase/src/models/typeObjects.ts +++ b/packages/apps/purchase/src/models/typeObjects.ts @@ -64,4 +64,25 @@ export const purchase_typeObjects: ModuleSelections = [ }, }, }, + { + modelName: 'com.axelor.apps.purchase.db.PurchaseRequestLine', + fields: { + newProduct: { + content: [ + { + key: 'NewProduct', + value: true, + title: 'Purchase_NewProduct', + color: 'plannedColor', + }, + { + key: 'ExistingProduct', + value: false, + title: 'Purchase_ExistingProduct', + color: 'secondaryColor', + }, + ], + }, + }, + }, ]; diff --git a/packages/apps/purchase/src/screens/RequestLineListScreen.tsx b/packages/apps/purchase/src/screens/RequestLineListScreen.tsx new file mode 100644 index 000000000..0b6808d2a --- /dev/null +++ b/packages/apps/purchase/src/screens/RequestLineListScreen.tsx @@ -0,0 +1,100 @@ +/* + * Axelor Business Solutions + * + * Copyright (C) 2024 Axelor (). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import React, {useMemo, useState} from 'react'; +import {ChipSelect, Screen} from '@axelor/aos-mobile-ui'; +import { + SearchListView, + useSelector, + useTranslator, + useTypeHelpers, + useTypes, +} from '@axelor/aos-mobile-core'; +import {RequestHeader, RequestLineCard} from '../components'; +import {searchPurchaseRequestLine} from '../features/purchaseRequestLineSlice'; + +const RequestLineListScreen = () => { + const I18n = useTranslator(); + const {PurchaseRequestLine} = useTypes(); + const {getSelectionItems} = useTypeHelpers(); + + const {purchaseRequest} = useSelector( + state => state.purchase_purchaseRequest, + ); + const { + loadingPurchaseRequestLines, + moreLoadingPurchaseRequestLine, + isListEndPurchaseRequestLine, + purchaseRequestLineList, + } = useSelector(state => state.purchase_purchaseRequestLine); + + const [selectedStatus, setSelectedStatus] = useState([]); + + const statusList = useMemo( + () => getSelectionItems(PurchaseRequestLine?.newProduct, selectedStatus), + [PurchaseRequestLine?.newProduct, getSelectionItems, selectedStatus], + ); + + const sliceFunctionData = useMemo( + () => ({ + purchaseRequestId: purchaseRequest.id, + newProduct: selectedStatus[0]?.value, + }), + [purchaseRequest.id, selectedStatus], + ); + + return ( + + } + actionList={[ + { + iconName: 'plus', + title: I18n.t('Purchase_AddProduct'), + onPress: () => {}, + }, + ]} + chipComponent={ + + } + expandableFilter={false} + searchPlaceholder={I18n.t('Base_Search')} + list={purchaseRequestLineList} + loading={loadingPurchaseRequestLines} + moreLoading={moreLoadingPurchaseRequestLine} + isListEnd={isListEndPurchaseRequestLine} + sliceFunction={searchPurchaseRequestLine} + sliceFunctionData={sliceFunctionData} + renderListItem={({item}) => ( + + )} + /> + + ); +}; + +export default RequestLineListScreen; diff --git a/packages/apps/purchase/src/screens/index.ts b/packages/apps/purchase/src/screens/index.ts index 940f41765..64fb44dfa 100644 --- a/packages/apps/purchase/src/screens/index.ts +++ b/packages/apps/purchase/src/screens/index.ts @@ -17,6 +17,7 @@ */ import RequestDetailsScreen from './RequestDetailsScreen'; +import RequestLineListScreen from './RequestLineListScreen'; import RequestListScreen from './RequestListScreen'; export default { @@ -29,13 +30,21 @@ export default { isUsableOnShortcut: true, }, RequestDetailsView: { - title: 'Purchase_Request', + title: 'Purchase_InternalRequest', component: RequestDetailsScreen, options: { shadedHeader: false, }, }, + RequestLineListScreen: { + title: 'Purchase_InternalRequest', + component: RequestLineListScreen, + options: { + shadedHeader: false, + }, + }, }; export {RequestDetailsScreen}; export {RequestListScreen}; +export {RequestLineListScreen};