From 9cb4302fc940c8680a6254f04e51138c93bd6d13 Mon Sep 17 00:00:00 2001 From: Luis Felipe Cadavid Date: Tue, 1 Oct 2024 13:45:34 -0500 Subject: [PATCH] feat: add sale flow with Boopos Cloud --- app/selfcheckout/checkout/page.tsx | 13 +- app/selfcheckout/precheckout/page.tsx | 26 +-- package/components/atoms/states/StoreAtom.tsx | 11 + .../components/layouts/AskForAffiliation.tsx | 54 ----- .../layouts/IdentityDocumentNumber.tsx | 111 ---------- package/components/layouts/MainLayout.tsx | 208 +++++++++++++++--- .../layouts/TypeIdentityDocument.tsx | 55 ----- package/components/molecules/SaleSummary.tsx | 38 +--- package/components/molecules/UserHeader.tsx | 14 +- .../organisms/Checkout/ClientSearch.tsx | 114 ++++++++-- .../organisms/Checkout/ControlPanel.tsx | 72 ++++-- .../organisms/Checkout/ProductTable.tsx | 84 +++---- .../api/Customer/TypeDNI/GetTypeDNI/page.tsx | 3 +- .../api/Sale/Transaction/GetSale/page.tsx | 2 +- .../api/Sale/Transaction/StartSale/page.tsx | 2 +- package/hooks/useClientSearchLogic.tsx | 8 +- .../Sale/GetSaleOutputInterface.tsx | 4 +- .../Sale/GetSaleResponseInterface.tsx | 26 +-- .../Sale/StartSaleResponseInterface.tsx | 2 +- 19 files changed, 449 insertions(+), 398 deletions(-) create mode 100644 package/components/atoms/states/StoreAtom.tsx delete mode 100644 package/components/layouts/AskForAffiliation.tsx delete mode 100644 package/components/layouts/IdentityDocumentNumber.tsx delete mode 100644 package/components/layouts/TypeIdentityDocument.tsx rename app/selfcheckout/clientSearch/page.tsx => package/components/organisms/Checkout/ClientSearch.tsx (68%) diff --git a/app/selfcheckout/checkout/page.tsx b/app/selfcheckout/checkout/page.tsx index 2734c3b..3f17a3c 100644 --- a/app/selfcheckout/checkout/page.tsx +++ b/app/selfcheckout/checkout/page.tsx @@ -1,9 +1,16 @@ "use client"; import { Box } from "@mui/material"; import React from "react"; +import { useTranslation } from "react-i18next"; import MainLayout from "@layouts/MainLayout"; -const CheckoutScreen: React.FC = () => { +interface CheckoutScreenProps { + onEndTransaction: () => void; // Callback para finalizar la transacción +} + +const CheckoutScreen: React.FC = ({ onEndTransaction }) => { + const { t } = useTranslation(); + return ( { boxShadow: '4' }} > - + ); }; -export default CheckoutScreen; \ No newline at end of file +export default CheckoutScreen; diff --git a/app/selfcheckout/precheckout/page.tsx b/app/selfcheckout/precheckout/page.tsx index ba88edd..c6eb964 100644 --- a/app/selfcheckout/precheckout/page.tsx +++ b/app/selfcheckout/precheckout/page.tsx @@ -1,31 +1,27 @@ "use client"; import { Box, Button, Container } from "@mui/material"; import Grid from "@mui/material/Grid2"; -import { useRouter } from "next/navigation"; -import React, { useCallback, useEffect } from "react"; +import React, { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; +import CheckoutScreen from "@app/selfcheckout/checkout/page"; import Title from "@atoms/Title"; const PreCheckout: React.FC = () => { const { t } = useTranslation(); - const router = useRouter(); + const [stage, setStage] = useState<"precheckout" | "searching" | "checkout">("precheckout"); const handleStartCheckout = useCallback(() => { - router.push("/selfcheckout/verifyAffiliation"); - }, [router]); + setStage("checkout"); + }, []); - useEffect(() => { - const handleKeyPress = () => { - handleStartCheckout(); - }; - document.addEventListener("keydown", handleKeyPress); + const handleEndTransaction = () => { + setStage("precheckout"); + }; - // Cleanup the event listener - return () => { - document.removeEventListener("keydown", handleKeyPress); - }; - }, [handleStartCheckout]); + if (stage === "checkout") { + return ; + } return ( diff --git a/package/components/atoms/states/StoreAtom.tsx b/package/components/atoms/states/StoreAtom.tsx new file mode 100644 index 0000000..022965d --- /dev/null +++ b/package/components/atoms/states/StoreAtom.tsx @@ -0,0 +1,11 @@ +import { atom } from 'jotai'; + +export const storeNumberAtom = atom(process.env.NEXT_PUBLIC_STORE_NUMBER || ''); +export const storePosAtom = atom(process.env.NEXT_PUBLIC_POS || ''); +export const storeInfoPosAtom = atom({ + addressPos: process.env.NEXT_PUBLIC_STORE_ADDRESS || '', + codeMunPos: process.env.NEXT_PUBLIC_STORE_CODE_CITY || '', + nameMunPos: process.env.NEXT_PUBLIC_STORE_CITY || '', + codeDepPos: process.env.NEXT_PUBLIC_STORE_CODE_DEPARTMENT || '', + nameDepPos: process.env.NEXT_PUBLIC_STORE_DEPARTMENT || '', +}); diff --git a/package/components/layouts/AskForAffiliation.tsx b/package/components/layouts/AskForAffiliation.tsx deleted file mode 100644 index ba6fcb0..0000000 --- a/package/components/layouts/AskForAffiliation.tsx +++ /dev/null @@ -1,54 +0,0 @@ -"use client" -import { Box, Card, CardContent } from "@mui/material" -import Grid from "@mui/material/Grid2" -import { useTranslation } from "react-i18next" -import Button from "@atoms/Button" -import Label from "@atoms/Label" -import Title from "@atoms/Title" - -interface AskForAffiliationProps { - onAffiliationSelect: (isAffiliated: boolean) => void -} - -const AskForAffiliation: React.FC = ({ onAffiliationSelect }) => { - - const { t } = useTranslation() - - const handleYesClick = () => { - onAffiliationSelect(true) - } - - const handleNoClick = () => { - onAffiliationSelect(false) - } - - return ( - - - - - <Label htmlFor="loyalty" text= {t("msj_are_you_registered_as_affiliate")} /> - <Box sx={{ mt: 3, display: "flex", gap: 3 }}> - <Grid size={{ xs: 6 }} sx={{ minWidth: "200px" }}> - <Button label={t("lbl_yes")} onClick={handleYesClick} variant="primary" className="h-16 w-full" /> - </Grid> - <Grid size={{ xs: 6 }} sx={{ minWidth: "200px" }}> - <Button label={t('lbl_no')} onClick={handleNoClick} variant="primary" className="h-16 w-full" /> - </Grid> - </Box> - </Box> - </CardContent> - </Card> - ) -} - -export default AskForAffiliation diff --git a/package/components/layouts/IdentityDocumentNumber.tsx b/package/components/layouts/IdentityDocumentNumber.tsx deleted file mode 100644 index 6cba5cd..0000000 --- a/package/components/layouts/IdentityDocumentNumber.tsx +++ /dev/null @@ -1,111 +0,0 @@ -"use client" -import { Box, Card, CardContent } from "@mui/material" -import Grid from "@mui/material/Grid2" -import React, { useEffect, useState } from "react" -import { useTranslation } from "react-i18next" -import Button from "@atoms/Button" -import Input from "@atoms/Input" -import Title from "@atoms/Title" - -interface IdentityDocumentNumberProps { - documentType: string - onSubmit: (documentNumber: string) => void -} - -const IdentityDocumentNumber: React.FC<IdentityDocumentNumberProps> = ({ documentType, onSubmit }) => { - const [inputValue, setInputValue] = useState<string>("") - const [keyboardMode, setKeyboardMode] = useState<"numeric" | "alpha">("numeric") - const [isAlphaEnabled, setIsAlphaEnabled] = useState<boolean>(false) - const { t } = useTranslation() - - - useEffect(() => { - const alphaDocuments = ["PASAPORTE", "CURP", "RFC", "NIE", "RUT"] - setIsAlphaEnabled(alphaDocuments.includes(documentType.toUpperCase())) - setKeyboardMode(isAlphaEnabled ? "alpha" : "numeric") - }, [documentType, isAlphaEnabled]) - - const handleKeyPress = (key: string) => { - setInputValue((prev) => prev + key) - } - - const handleDelete = () => { - setInputValue((prev) => prev.slice(0, -1)) - } - - const renderKeyboard = () => { - if (keyboardMode === "numeric") { - return ( - <> - {["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"].map((num) => ( - <Grid key={num} size={{ xs: 4 }}> - <Button label={num} onClick={() => handleKeyPress(num)} variant="primary" className="h-16 w-full" /> - </Grid> - ))} - </> - ) - } else { - return ( - <> - {"ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("").map((letter) => ( - <Grid key={letter} size={{ xs: 2 }}> - <Button label={letter} onClick={() => handleKeyPress(letter)} variant="primary" className="h-16 w-full" /> - </Grid> - ))} - </> - ) - } - } - - return ( - <Card sx={{ width: "100%", maxWidth: "100%" }}> - <CardContent> - <Box - sx={{ - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - height: "100%", - p: 2, - }} - > - {/* Título */} - <Title text={`${t('msj_enter_number_of')} ${documentType}`} size="h4" /> - - <Input - type="text" - placeholder= {t('msj_enter_document_number')} - value={inputValue} - onChange={(e) => setInputValue(e.target.value)} - /> - - <Grid container spacing={2} justifyContent="center" alignItems="center" sx={{ mt: 3 }}> - <Grid size={{ xs: 4 }}> - <Button label= {t('lbl_delete')} onClick={handleDelete} variant="danger" className="h-16 w-full" /> - </Grid> - <Grid size={{ xs: 4 }}> - <Button label={t('lbl_cancel')} onClick={() => setInputValue("")} variant="danger" className="h-16 w-full" /> - </Grid> - {renderKeyboard()} - <Grid size={{ xs: 4 }}> - <Button label={t('lbl_enter')} onClick={() => onSubmit(inputValue)} variant="success" className="h-16 w-full" /> - </Grid> - {isAlphaEnabled && ( - <Grid size={{ xs: 4 }}> - <Button - label={`${t('msj_change_to')} ${keyboardMode === "numeric" ? t('lbl_alphabetical') : t('lbl_numeric')}`} - onClick={() => setKeyboardMode(keyboardMode === "numeric" ? "alpha" : "numeric")} - variant="info" - className="h-16 w-full" - /> - </Grid> - )} - </Grid> - </Box> - </CardContent> - </Card> - ) -} - -export default IdentityDocumentNumber diff --git a/package/components/layouts/MainLayout.tsx b/package/components/layouts/MainLayout.tsx index ebe443b..be3ade5 100644 --- a/package/components/layouts/MainLayout.tsx +++ b/package/components/layouts/MainLayout.tsx @@ -1,55 +1,207 @@ "use client"; -import { Box, Container, Paper } from '@mui/material'; +import { Box, Container, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Paper } from '@mui/material'; import Grid from '@mui/material/Grid2'; -import React from 'react'; +import { useAtom } from 'jotai'; +import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import Button from '@atoms/Button'; +import { storeNumberAtom, storePosAtom } from '@atoms/states/StoreAtom'; +import { EndSaleOutputInterface } from '@interfaces/OutputInterfaces/Sale/EndSaleOutputInterface'; +import { GetSaleOutputInterface } from '@interfaces/OutputInterfaces/Sale/GetSaleOutputInterface'; +import { CustomerInterface } from '@interfaces/ResponseInterfaces/Customer/CustomerResponseInterface'; +import { EndSaleResponseInterface } from '@interfaces/ResponseInterfaces/Sale/EndSaleResponseInterface'; +import { GetSaleResponseInterface, SaleTransactionObjectInterface } from '@interfaces/ResponseInterfaces/Sale/GetSaleResponseInterface'; +import { StartSaleResponseInterface } from '@interfaces/ResponseInterfaces/Sale/StartSaleResponseInterface'; +import ClientSearch from '@organisms/Checkout/ClientSearch'; import ControlPanel from '@organisms/Checkout/ControlPanel'; import ProductTable from '@organisms/Checkout/ProductTable'; +import EndSale from '@package/config/api/Sale/Transaction/EndSale/page'; +import GetSale from '@package/config/api/Sale/Transaction/GetSale/page'; +import { getUser } from '@utils/Utilities'; -const products = [ - { id: 1, amount: 2, name: "Product X", unitValue: 2500, tax: 110, discount: 0, total: 5220 }, - { id: 2, amount: 1, name: "Product Y", unitValue: 3500, tax: 230, discount: 0, total: 3730 }, - { id: 3, amount: 3, name: "Product Z", unitValue: 1000, tax: 100, discount: 0, total: 3300 }, -]; +interface MainLayoutProps { + onEndTransaction: () => void; // Callback para notificar cuando la transacción termine +} + +const MainLayout: React.FC<MainLayoutProps> = ({ onEndTransaction }) => { + const [transaction, setTransaction] = useState<StartSaleResponseInterface | null>(null); + const [client, setClient] = useState<CustomerInterface | null>(null); + const [productsList, setProductsList] = useState<SaleTransactionObjectInterface | null>(null); + const [endSaleResponse, setEndSaleResponse] = useState<EndSaleResponseInterface | null>(null); + const [openDialog, setOpenDialog] = useState(false); + + const [idUser] = useState(getUser() && getUser().id ? getUser().id : ''); + const { t } = useTranslation(); + + const [storeNumber] = useAtom(storeNumberAtom); + const [storePos] = useAtom(storePosAtom); + + const handleTransactionComplete = (transaction: StartSaleResponseInterface, client: CustomerInterface) => { + setTransaction(transaction); + setClient(client); + }; + + const fetchSaleDetails = useCallback(async () => { + if (!transaction || !client) return; + + try { + const saleRequest: GetSaleOutputInterface = { + idTransaction: transaction.object?.id, + storeNumber: storeNumber, + idSubsidiary: client.idSubsidiary, + idUser: client.idUser, + pos: storePos, + }; + + const saleResponse = await GetSale(saleRequest) as GetSaleResponseInterface; + + if (saleResponse && saleResponse.correct) { + setProductsList(saleResponse.object); + } else { + console.error("Error fetching sale details: ", saleResponse?.message); + } + } catch (error) { + console.error("Error during GetSale API call", error); + } + }, [client, storeNumber, storePos, transaction]); + + useEffect(() => { + fetchSaleDetails(); + }, [fetchSaleDetails, transaction]); + + const handleProductAdded = () => { + fetchSaleDetails(); + }; -const MainLayout: React.FC = () => { const handleVoidOrder = () => console.log("Void Order"); const handleVoidLast = () => console.log("Void Last"); const handleResume = () => console.log("Resume"); - const handleSearchPLU = (plu: string) => console.log("Search PLU:", plu); - const subtotal = products.reduce((acc, product) => acc + product.unitValue * product.amount, 0); - const discount = products.reduce((acc, product) => acc + product.discount, 0); - const tax = products.reduce((acc, product) => acc + product.tax, 0); - const total = products.reduce((acc, product) => acc + product.total, 0); + const handleEndSale = async () => { + if (!transaction || !client || !productsList) return; + + if (!transaction.object?.id) { + console.error("Transaction ID is missing"); + return; + } + + if (client.idSubsidiary === undefined) { + console.error("Client subsidiary ID is missing"); + return; + } + const storeAddress = process.env.NEXT_PUBLIC_STORE_ADDRESS || ''; + const codeMunPos = process.env.NEXT_PUBLIC_STORE_CODE_CITY || ''; + const nameMunPos = process.env.NEXT_PUBLIC_STORE_CITY || ''; + const codeDepPos = process.env.NEXT_PUBLIC_STORE_CODE_DEPARTMENT || ''; + const nameDepPos = process.env.NEXT_PUBLIC_STORE_DEPARTMENT || ''; + + if (!storeAddress || !codeMunPos || !nameMunPos || !codeDepPos || !nameDepPos) { + console.error("Missing store address information from environment variables"); + return; + } + const endSaleRequest: EndSaleOutputInterface = { + idTransaction: transaction.object.id, + storeNumber: storeNumber, + idSubsidiary: client.idSubsidiary, + idUser: idUser, + localDate: new Date().toISOString(), + pos: storePos, + comment: 'Test End Sale', + payment: [ + { + type: "CASH", + approvalCode: "0001", + receiptNumber: "10001", + value: productsList.totalWithPromotion+ productsList.totalTaxes, + cardNumber: "0032", + cardType: "DEBIT", + comment: "Test" + } + ], + infoPos: { + addressPos: storeAddress, + codeMunPos: codeMunPos, + nameMunPos: nameMunPos, + codeDepPos: codeDepPos, + nameDepPos: nameDepPos, + pos: storePos + } + }; + console.log("End Sale Request: ", endSaleRequest); + try { + const endSaleResponse = await EndSale(endSaleRequest) as EndSaleResponseInterface; + + console.log("End Sale Response: ", endSaleResponse); + if (endSaleResponse && endSaleResponse.correct) { + setEndSaleResponse(endSaleResponse); + setOpenDialog(true); + } else { + console.error("Error ending sale:", endSaleResponse?.message); + } + } catch (error) { + console.error("Error during EndSale API call", error); + } + }; + + const handleDialogClose = () => { + setOpenDialog(false); + onEndTransaction(); + }; return ( <Box sx={{ display: 'flex', flexDirection: 'column', bgcolor: 'background.default', boxShadow: '4' }}> <Container maxWidth={false} sx={{ flexGrow: 1, display: 'flex', py: 2 }}> <Grid container spacing={2}> - <Grid size={{xs:12, md:8}}> - <Paper elevation={3} sx={{ height: '100%', p: 2}}> - <ProductTable products={products} /> + <Grid size={{ xs: 12, md: 8 }}> + <Paper elevation={3} sx={{ height: '100%', p: 2 }}> + <ProductTable products={productsList} /> + </Paper> </Grid> - <Grid size={{xs:12, md:4}}> + <Grid size={{ xs: 12, md: 4 }}> <Paper elevation={3} sx={{ height: '100%', p: 2 }}> - <ControlPanel - onVoidOrder={handleVoidOrder} - onVoidLast={handleVoidLast} - onResume={handleResume} - onSearchPLU={handleSearchPLU} - subtotal={subtotal} - discount={discount} - tax={tax} - total={total} - /> + {!transaction && !client ? ( + <ClientSearch onTransactionComplete={handleTransactionComplete} /> + ) : ( + <ControlPanel + onVoidOrder={handleVoidOrder} + onVoidLast={handleVoidLast} + onResume={handleResume} + transaction={transaction!} + client={client!} + onProductAdded={handleProductAdded} + /> + + )} + {productsList && productsList.list.length > 0 && ( + <Button + label={t("lbl_resume")} + variant="primary" + onClick={handleEndSale} + className="full-width-button" + disabled={!productsList || productsList.list.length === 0} + /> + )} + </Paper> </Grid> </Grid> </Container> + + <Dialog open={openDialog} onClose={handleDialogClose}> + <DialogTitle>{t("transaction_complete")}</DialogTitle> + <DialogContent> + <DialogContentText> + {endSaleResponse?.object?.description || t("transaction_successful")} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={handleDialogClose} label="Aceptar" /> + </DialogActions> + </Dialog> </Box> ); }; -export default MainLayout; \ No newline at end of file +export default MainLayout; diff --git a/package/components/layouts/TypeIdentityDocument.tsx b/package/components/layouts/TypeIdentityDocument.tsx deleted file mode 100644 index 0659268..0000000 --- a/package/components/layouts/TypeIdentityDocument.tsx +++ /dev/null @@ -1,55 +0,0 @@ -"use client" -import { Box, Card, CardContent } from "@mui/material" -import Grid from "@mui/material/Grid2" -import { useTranslation } from "react-i18next" -import Button from "@atoms/Button" -import Label from "@atoms/Label" -import Title from "@atoms/Title" - -interface TypesDocumentProps { - typesDocument: string[] - onDocumentSelect: (documentType: string) => void -} - -const TypeIdentityDocument: React.FC<TypesDocumentProps> = ({ typesDocument, onDocumentSelect }) => { - const { t } = useTranslation() - - const handleDocumentClick = (documentType: string) => { - onDocumentSelect(documentType) - } - - return ( - <Card sx={{ width: "100%", maxWidth: "100%" }}> - <CardContent> - <Box - sx={{ - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - height: "100%", - p: 1, - }} - > - <Title text= {t("lbl_information_required")} size="h4" /> - <Label htmlFor="loyalty" text={t('msj_select_type_identity_document')} /> - - <Grid container spacing={1} justifyContent="center" sx={{ mt: 2 }}> - {typesDocument.map((documentType, index) => ( - <Grid key={index} size={{ xs: 4, md: 6 }} sx={{ minWidth: "100px" }}> - <Button - label={documentType} - onClick={() => handleDocumentClick(documentType)} - variant="primary" - className="h-16 w-full" - /> - </Grid> - ))} - </Grid> - </Box> - </CardContent> - </Card> - ) -} - -export default TypeIdentityDocument diff --git a/package/components/molecules/SaleSummary.tsx b/package/components/molecules/SaleSummary.tsx index 6388dd7..5d103ee 100644 --- a/package/components/molecules/SaleSummary.tsx +++ b/package/components/molecules/SaleSummary.tsx @@ -5,15 +5,14 @@ import Grid from '@mui/material/Grid2'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { ItemObject } from '@interfaces/ResponseInterfaces/Sale/AddProductsResponseInterface'; interface SaleSummaryProps { - subtotal: number; - discount: number; - tax: number; - total: number; + product: ItemObject; + } -const SaleSummary: React.FC<SaleSummaryProps> = ({ subtotal, discount, tax, total }) => { +const SaleSummary: React.FC<SaleSummaryProps> = ({ product }) => { const { t } = useTranslation() return ( @@ -30,15 +29,13 @@ const SaleSummary: React.FC<SaleSummaryProps> = ({ subtotal, discount, tax, tota <TableHead> <TableRow> <TableCell sx={{ color: 'primary', borderColor: 'black', fontWeight: 'bold', width: '30%' }}>{t("tle_product")}</TableCell> - <TableCell sx={{ color: 'primary', borderColor: 'black', fontWeight: 'bold', width: '50%' }}>{t("tle_description")}</TableCell> <TableCell sx={{ color: 'primary', borderColor: 'black', fontWeight: 'bold', width: '20%', textAlign: 'right' }}>{t("tle_amount")}</TableCell> </TableRow> </TableHead> <TableBody> <TableRow> - <TableCell sx={{ color: 'black', borderColor: 'black' }}>Product R</TableCell> - <TableCell sx={{ color: 'black', borderColor: 'black' }}>Producto para r uso</TableCell> - <TableCell sx={{ color: 'black', borderColor: 'black', textAlign: 'right' }}>3</TableCell> + <TableCell sx={{ color: 'black', borderColor: 'black' }}>{product.name}</TableCell> + <TableCell sx={{ color: 'black', borderColor: 'black', textAlign: 'right' }}>1</TableCell> </TableRow> </TableBody> </Table> @@ -49,31 +46,16 @@ const SaleSummary: React.FC<SaleSummaryProps> = ({ subtotal, discount, tax, tota <Typography variant="body2" align="right">{t("lbl_subtotal")}:</Typography> </Grid> <Grid size={{xs:5}}> - <Typography variant="body2" align="left">$ 1.400</Typography> - </Grid> - </Grid> - <Grid container> - <Grid size={{xs:7}} sx={{ pr: 10 }}> - <Typography variant="body2" align="right">{t("lbl_discount")}:</Typography> - </Grid> - <Grid size={{xs:5}}> - <Typography variant="body2" align="left">$ 12</Typography> + <Typography variant="body2" align="left">{product.regularUnitPrice}</Typography> </Grid> </Grid> + <Grid container> <Grid size={{xs:7}} sx={{ pr: 5.5 }}> <Typography variant="body2" align="right">{t("lbl_tax")}:</Typography> </Grid> <Grid size={{xs:5}}> - <Typography variant="body2" align="left">$ 123</Typography> - </Grid> - </Grid> - <Grid container> - <Grid size={{xs:7}} sx={{ pr: 5.5 }}> - <Typography variant="body2" align="right">{t("lbl_total")}:</Typography> - </Grid> - <Grid size={{xs:5}}> - <Typography variant="body2" align="left">$ 1.511</Typography> + <Typography variant="body2" align="left">{product.totalTaxes}</Typography> </Grid> </Grid> </Box> @@ -82,7 +64,7 @@ const SaleSummary: React.FC<SaleSummaryProps> = ({ subtotal, discount, tax, tota <Box sx={{ p: 3, boxShadow: '2' }}> <Typography variant="h6" sx={{ color: 'primary', display: 'flex', justifyContent: 'space-between' }}> <span>{t("tle_total")}:</span> - <span>$ 12.250</span> + <span>{product.extendedPrice}</span> </Typography> </Box> </Box> diff --git a/package/components/molecules/UserHeader.tsx b/package/components/molecules/UserHeader.tsx index 835403b..0a43c93 100644 --- a/package/components/molecules/UserHeader.tsx +++ b/package/components/molecules/UserHeader.tsx @@ -6,10 +6,10 @@ import Typography from '@mui/material/Typography'; import { useAtom } from 'jotai'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import UserAtom from '@atoms/states/UserAtom'; +import CustomerAtom from '@atoms/states/CustomerAtom'; -const UserHeader: React.FC = () => { - const [user] = useAtom(UserAtom); +const CustomerHeader: React.FC = () => { + const [customer] = useAtom(CustomerAtom); const { t } = useTranslation() @@ -23,19 +23,19 @@ const UserHeader: React.FC = () => { > <Grid2> <Typography variant="h6" className="font-bold"> - {user ? `${t("lbl_name_customer")}: ${user.name} ${user.lastName}` : `${t("lbl_name_customer")}: Anónimo`} + {customer ? `${t("lbl_name_customer")}: ${customer.name} ${customer.firstSurname}` : `${t("lbl_name_customer")}: Anónimo`} </Typography> </Grid2> <Grid2> <Typography variant="body2" color="textSecondary" className="text-white"> - {user ? `${t("lbl_dni_customer")}: ${user.idTypeUser}` : `${t("lbl_dni_customer")}: 222222222222`} + {customer ? `${t("lbl_dni_customer")}: ${customer.dni}` : `${t("lbl_dni_customer")}: 222222222222`} </Typography> </Grid2> <Grid2> <Typography variant="body2" className="text-white"> - {user ? `${t("lbl_email_customer")}: ${user.email}` : `${t("lbl_email_customer")}: generic@example.com`} + {customer ? `${t("lbl_email_customer")}: ${customer.mail}` : `${t("lbl_email_customer")}: generic@example.com`} </Typography> </Grid2> </Grid2> @@ -43,4 +43,4 @@ const UserHeader: React.FC = () => { ); }; -export default UserHeader; +export default CustomerHeader; diff --git a/app/selfcheckout/clientSearch/page.tsx b/package/components/organisms/Checkout/ClientSearch.tsx similarity index 68% rename from app/selfcheckout/clientSearch/page.tsx rename to package/components/organisms/Checkout/ClientSearch.tsx index 6b4cca4..a73253f 100644 --- a/app/selfcheckout/clientSearch/page.tsx +++ b/package/components/organisms/Checkout/ClientSearch.tsx @@ -1,23 +1,29 @@ 'use client'; import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Paper, Typography } from '@mui/material'; import Grid from '@mui/material/Grid2'; -import { useSetAtom } from 'jotai'; -import { useRouter } from 'next/navigation'; +import { useAtom } from 'jotai'; import React, { useState } from 'react'; - import { useTranslation } from 'react-i18next'; import Button from '@atoms/Button'; import CustomLoading from '@atoms/CustomLoading'; import Icon from '@atoms/Icon'; import Input from '@atoms/Input'; import Label from '@atoms/Label'; -import CustomerAtom from '@atoms/states/CustomerAtom'; +import { storeNumberAtom, storePosAtom } from '@atoms/states/StoreAtom'; import Title from '@atoms/Title'; import { Tooltip } from '@atoms/Tooltip'; import { useClientSearchLogic } from '@hooks/useClientSearchLogic'; +import { CustomerInterface } from '@interfaces/ResponseInterfaces/Customer/CustomerResponseInterface'; import { CustomerDniTypeInterface } from '@interfaces/ResponseInterfaces/Customer/DNITypeResponseInterface'; +import { StartSaleResponseInterface } from '@interfaces/ResponseInterfaces/Sale/StartSaleResponseInterface'; +import StartSale from '@package/config/api/Sale/Transaction/StartSale/page'; +import { getUser } from '@utils/Utilities'; -const ClientSearch: React.FC = () => { +interface ClientSearchProps { + onTransactionComplete: (transaction: StartSaleResponseInterface, client: CustomerInterface) => void; +} + +const ClientSearch: React.FC<ClientSearchProps> = ({ onTransactionComplete }) => { const { idTypes, loading, @@ -27,11 +33,15 @@ const ClientSearch: React.FC = () => { handleInputChange, handleSubmitForm, isAffiliated, + customer, } = useClientSearchLogic(); + const { t } = useTranslation(); const [openDialog, setOpenDialog] = useState(false); - const setCustomer = useSetAtom(CustomerAtom); - const router = useRouter(); + const [idUser] = useState(getUser() && getUser().id ? getUser().id : ''); + + const [storeNumber] = useAtom(storeNumberAtom); + const [storePos] = useAtom(storePosAtom); const handleOpenDialog = () => { setOpenDialog(true); @@ -41,20 +51,20 @@ const ClientSearch: React.FC = () => { setOpenDialog(false); }; - const handleUseGenericCustomer = () => { - setCustomer({ + const handleUseGenericCustomer = async () => { + const genericCustomer = { id: '', idCompany: 'default-company', companyName: 'default-company-name', - idSubsidiary: 0, + idSubsidiary: 3, nameSubsidiary: 'default-subsidiary', externalClientId: null, name: 'Cliente', secondName: '', firstSurname: 'Generico', secondSurname: '', - idTypeCustomer: 'Tarjeta de identidad', - nameTypeCustomer: 'Tarjeta', + idTypeCustomer: '12', + nameTypeCustomer: 'Tarjeta de identidad', dni: '222222222222', idGender: '', nameGender: '', @@ -74,14 +84,75 @@ const ClientSearch: React.FC = () => { group: [], NameSurname: '', SurnameName: '' - }); + }; - router.push("/selfcheckout/checkout"); + const saleData = { + storeNumber: storeNumber, + idSubsidiary: genericCustomer.idSubsidiary, + idClient: genericCustomer.id, + clientDni: genericCustomer.dni, + idType: genericCustomer.idTypeCustomer, + idGroup: '', + invoiceName: genericCustomer.name, + invoiceTypeNit: genericCustomer.idTypeCustomer, + invoiceNit: genericCustomer.dni, + invoiceDirection: genericCustomer.address, + invoiceCellPhone: genericCustomer.phone, + invoiceEmail: genericCustomer.mail, + idUser: idUser, + localDate: new Date().toISOString(), + pos: storePos, + }; + + const saleResponse = await StartSale(saleData) as StartSaleResponseInterface; + + if (saleResponse && saleResponse.correct) { + if (customer) { + onTransactionComplete(saleResponse, customer); + } else { + console.error("Customer is null"); + } + } else { + console.error(saleResponse.message); + } }; const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); // Evita el comportamiento por defecto del formulario - handleSubmitForm(); // Ejecuta la función de búsqueda con los datos actuales + e.preventDefault(); + handleSubmitForm(); + }; + + const handleContinue = async () => { + if (customer) { + const saleData = { + storeNumber: storeNumber, + idSubsidiary: customer.idSubsidiary || 0, + idClient: customer.id, + clientDni: customer.dni, + idType: customer.idTypeCustomer, + idGroup: '', + invoiceName: customer.name, + invoiceTypeNit: customer.idTypeCustomer, + invoiceNit: customer.dni, + invoiceDirection: customer.address, + invoiceCellPhone: customer.phone, + invoiceEmail: customer.mail, + idUser: idUser, + localDate: new Date().toISOString(), + pos: storePos, + }; + console.log("VALOR DE SALEDATA",saleData); + const saleResponse = await StartSale(saleData) as StartSaleResponseInterface; + + console.log("VALOR DE SALESRESPONSE",saleResponse); + if (saleResponse && saleResponse.correct) { + onTransactionComplete(saleResponse, customer); + } else { + console.error(saleResponse.message); + } + } else { + handleOpenDialog(); + } }; return ( @@ -95,7 +166,7 @@ const ClientSearch: React.FC = () => { <select name="idType" value={formData.idType} - onChange={handleInputChange} // Asegura que el estado del input cambie + onChange={handleInputChange} className="w-full p-2 border rounded" disabled={loading} > @@ -119,7 +190,7 @@ const ClientSearch: React.FC = () => { name="idNumber" placeholder={t("msj_enter_id_number")} value={formData.idNumber} - onChange={handleInputChange} // Actualiza el valor del input al escribir + onChange={handleInputChange} disabled={loading} /> </Grid> @@ -166,7 +237,6 @@ const ClientSearch: React.FC = () => { label={t("lbl_create_new_customer")} variant="primary" onClick={() => { - // Lógica para crear nuevo cliente }} /> </div> @@ -209,14 +279,12 @@ const ClientSearch: React.FC = () => { <Button label={t("lbl_continue")} variant="primary" - onClick={() => { - router.push("/selfcheckout/checkout"); - }} + onClick={handleContinue} /> </div> </Paper> )} - + <Dialog open={openDialog} onClose={handleCloseDialog} maxWidth="xs" fullWidth> <DialogTitle sx={{ textAlign: 'center' }}> {t("msj_confirm_generic_customer")} diff --git a/package/components/organisms/Checkout/ControlPanel.tsx b/package/components/organisms/Checkout/ControlPanel.tsx index c12cfbc..041d88a 100644 --- a/package/components/organisms/Checkout/ControlPanel.tsx +++ b/package/components/organisms/Checkout/ControlPanel.tsx @@ -1,32 +1,78 @@ "use client"; import Grid2 from '@mui/material/Grid2'; -import React from 'react'; +import { useAtom } from 'jotai'; +import React, { useState } from 'react'; +import { storeNumberAtom, storePosAtom } from '@atoms/states/StoreAtom'; +import { AddProductsOutputInterface } from '@interfaces/OutputInterfaces/Sale/AddProductsOutputInterfae'; +import { CustomerInterface } from '@interfaces/ResponseInterfaces/Customer/CustomerResponseInterface'; +import { AddProductsResponseInterface, ItemObject } from '@interfaces/ResponseInterfaces/Sale/AddProductsResponseInterface'; +import { StartSaleResponseInterface } from '@interfaces/ResponseInterfaces/Sale/StartSaleResponseInterface'; import ActionButtons from '@molecules/ActionButtons'; import ProductSearch from '@molecules/ProductSearch'; import SaleSummary from '@molecules/SaleSummary'; +import AddProducts from "@package/config/api/Sale/Transaction/AddProducts/page"; interface ControlPanelProps { onVoidOrder: () => void; onVoidLast: () => void; onResume: () => void; - onSearchPLU: (plu: string) => void; - subtotal: number; - discount: number; - tax: number; - total: number; + transaction: StartSaleResponseInterface; + client: CustomerInterface; + onProductAdded: () => void; } const ControlPanel: React.FC<ControlPanelProps> = ({ onVoidOrder, onVoidLast, onResume, - onSearchPLU, - subtotal, - discount, - tax, - total + transaction, + client, + onProductAdded, }) => { + + const [addedProducts, setAddedProducts] = useState<ItemObject>({} as ItemObject); + const [loading, setLoading] = useState(false); + const [storeNumber] = useAtom(storeNumberAtom); + const [storePos] = useAtom(storePosAtom); + + + const handleSearchSKU = async (sku: string) => { + setLoading(true); + + try { + const addProductRequest: AddProductsOutputInterface = { + idTransaction: transaction.object?.id, + storeNumber: storeNumber, + idSubsidiary: client.idSubsidiary, + clientNit: client.dni, + idUser: client.idUser, + localDate: new Date(), + pos: storePos, + products: [ + { + sku: sku, + value: 1, + }, + ], + }; + + const productResponse = await AddProducts(addProductRequest) as AddProductsResponseInterface; + + if (productResponse && productResponse.correct) { + const newProduct = productResponse.object; + setAddedProducts(newProduct) + onProductAdded(); + } else { + console.error("Error adding product: ", productResponse?.message); + } + } catch (error) { + console.error("Error during AddProducts API call", error); + } finally { + setLoading(false); + } + }; + return ( <Grid2 container spacing={4}> <Grid2 size={{xs:12}}> @@ -34,11 +80,11 @@ const ControlPanel: React.FC<ControlPanelProps> = ({ </Grid2> <Grid2 size={{xs:12}}> - <ProductSearch onSearch={onSearchPLU} /> + <ProductSearch onSearch={handleSearchSKU} /> </Grid2> <Grid2 size={{xs:12}}> - <SaleSummary subtotal={subtotal} discount={discount} tax={tax} total={total} /> + <SaleSummary product={addedProducts} /> </Grid2> </Grid2> ); diff --git a/package/components/organisms/Checkout/ProductTable.tsx b/package/components/organisms/Checkout/ProductTable.tsx index a63c5c5..034406e 100644 --- a/package/components/organisms/Checkout/ProductTable.tsx +++ b/package/components/organisms/Checkout/ProductTable.tsx @@ -3,30 +3,16 @@ import { Box, Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { SaleTransactionObjectInterface } from '@interfaces/ResponseInterfaces/Sale/GetSaleResponseInterface'; import CustomCard from '@molecules/CustomCard'; import UserHeader from '@molecules/UserHeader'; -interface Product { - id: number; - amount: number; - name: string; - unitValue: number; - tax: number; - discount: number; - total: number; -} - interface ProductTableProps { - products: Product[]; + products: SaleTransactionObjectInterface | null; } const ProductTable: React.FC<ProductTableProps> = ({ products }) => { - const totalAmount = products.reduce((acc, product) => acc + product.amount, 0); - const totalUnitValue = products.reduce((acc, product) => acc + product.unitValue * product.amount, 0); - const totalTax = products.reduce((acc, product) => acc + product.tax, 0); - const totalDiscount = products.reduce((acc, product) => acc + product.discount, 0); - const grandTotal = products.reduce((acc, product) => acc + product.total, 0); - const { t } = useTranslation() + const { t } = useTranslation(); return ( <Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}> @@ -48,38 +34,60 @@ const ProductTable: React.FC<ProductTableProps> = ({ products }) => { </TableRow> </TableHead> <TableBody> - {products.map((product) => ( - <TableRow key={product.id}> - <TableCell>{product.amount}</TableCell> - <TableCell>{product.name}</TableCell> - <TableCell>${product.unitValue.toFixed(2)}</TableCell> - <TableCell>${product.tax.toFixed(2)}</TableCell> - <TableCell>${product.discount.toFixed(2)}</TableCell> - <TableCell>${product.total.toFixed(2)}</TableCell> + {products?.list?.length ? ( + products.list.map((product) => ( + <TableRow key={product.idDetail}> + <TableCell>{product.quantity}</TableCell> + <TableCell>{product.name}</TableCell> + <TableCell>${product.regularUnitPrice.toFixed(2)}</TableCell> + <TableCell>${product.totalTaxes.toFixed(2)}</TableCell> + <TableCell>${product.discount.toFixed(2)}</TableCell> + <TableCell>${product.extendedPrice.toFixed(2)}</TableCell> + </TableRow> + )) + ) : ( + <TableRow> + <TableCell colSpan={6} align="center"> + {t("msj_no_products_added")} {/* Mensaje de no productos */} + </TableCell> </TableRow> - ))} + )} </TableBody> </Table> </Box> - <Box sx={{ p:0.2, boxShadow: '2', }}> + <Box sx={{ p: 0.2, boxShadow: '2' }}> <CustomCard className="bg-gray-700 text-white"> <Table> <TableHead> <TableRow> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with', fontWeight: 'bold', width: '20%' }}>{t("tle_item_count")}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with', fontWeight: 'bold', width: '20%' }}>{t("tle_total_unit")}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with', fontWeight: 'bold', width: '20%' }}>{t("tle_total_taxes")}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with', fontWeight: 'bold', width: '20%' }}>{t("tle_total_discount")}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with', fontWeight: 'bold', width: '20%' }}>{t("tle_total")}</TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)', fontWeight: 'bold', width: '20%' }}> + {t("tle_total_unit")} + </TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)', fontWeight: 'bold', width: '20%' }}> + {t("tle_total_taxes")} + </TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)', fontWeight: 'bold', width: '20%' }}> + {t("tle_total_discount")} + </TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)', fontWeight: 'bold', width: '20%' }}> + {t("tle_total")} + </TableCell> </TableRow> </TableHead> <TableBody> <TableRow> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with' }}>{totalAmount}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with' }}>${totalUnitValue.toFixed(2)}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with' }}>${totalTax.toFixed(2)}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with' }}>${totalDiscount.toFixed(2)}</TableCell> - <TableCell sx={{ color: 'RGB(255,255,255)', borderColor: 'with' }}>${grandTotal.toFixed(2)}</TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)' }}> + ${products?.totalSale?.toFixed(2) || '0.00'} + </TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)' }}> + ${products?.totalTaxes?.toFixed(2) || '0.00'} + </TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)' }}> + $0.00 {/* Descuento total si aplica */} + </TableCell> + <TableCell sx={{ color: 'RGB(255,255,255)' }}> + ${((products?.totalWithPromotion ?? 0) + (products?.totalTaxes ?? 0)).toFixed(2)} + </TableCell> </TableRow> </TableBody> </Table> @@ -89,4 +97,4 @@ const ProductTable: React.FC<ProductTableProps> = ({ products }) => { ); }; -export default ProductTable; \ No newline at end of file +export default ProductTable; diff --git a/package/config/api/Customer/TypeDNI/GetTypeDNI/page.tsx b/package/config/api/Customer/TypeDNI/GetTypeDNI/page.tsx index c5545d7..04dd164 100644 --- a/package/config/api/Customer/TypeDNI/GetTypeDNI/page.tsx +++ b/package/config/api/Customer/TypeDNI/GetTypeDNI/page.tsx @@ -1,4 +1,5 @@ import { GetTypeDNIOutputInterface } from "@interfaces/OutputInterfaces/Customer/GetTypeDNIOutputInterface"; +import { CustomerDniTypeResponseInterface } from "@interfaces/ResponseInterfaces/Customer/DNITypeResponseInterface"; import { BASE_URL_CUSTOMER_TYPEDNI } from "@package/config/ApiPath"; import { FetchData } from "@package/config/FetchData"; import { ReturnService } from "@package/config/ReturnService" @@ -36,7 +37,7 @@ async function GetTypeDni(ae_object: GetTypeDNIOutputInterface) { }) } - const data = await FetchData(BASE_URL_CUSTOMER_TYPEDNI, 'all', options) + const data: CustomerDniTypeResponseInterface = await FetchData(BASE_URL_CUSTOMER_TYPEDNI, 'all', options) if (data.correct) { return data.object diff --git a/package/config/api/Sale/Transaction/GetSale/page.tsx b/package/config/api/Sale/Transaction/GetSale/page.tsx index 1797e2c..16a4677 100644 --- a/package/config/api/Sale/Transaction/GetSale/page.tsx +++ b/package/config/api/Sale/Transaction/GetSale/page.tsx @@ -31,7 +31,7 @@ async function GetSale(ae_object: GetSaleOutputInterface) { }) } - let data: GetSaleResponseInterface = await FetchData(BASE_URL_SALE, 'addProducts', options) + let data: GetSaleResponseInterface = await FetchData(BASE_URL_SALE, 'getSale', options) if (data.correct) { return data.object diff --git a/package/config/api/Sale/Transaction/StartSale/page.tsx b/package/config/api/Sale/Transaction/StartSale/page.tsx index 444b68f..739e0a8 100644 --- a/package/config/api/Sale/Transaction/StartSale/page.tsx +++ b/package/config/api/Sale/Transaction/StartSale/page.tsx @@ -42,7 +42,7 @@ async function StartSale(ae_object: StartSaleOutputInterface) { }) } - let data : StartSaleResponseInterface = await FetchData(BASE_URL_SALE, 'addProducts', options) + let data : StartSaleResponseInterface = await FetchData(BASE_URL_SALE, 'startSale', options) if (data.correct) { return data.object diff --git a/package/hooks/useClientSearchLogic.tsx b/package/hooks/useClientSearchLogic.tsx index f266a17..4c80cba 100644 --- a/package/hooks/useClientSearchLogic.tsx +++ b/package/hooks/useClientSearchLogic.tsx @@ -1,6 +1,4 @@ -import { useSetAtom } from 'jotai'; import { useEffect, useState } from 'react'; -import CustomerAtom from '@atoms/states/CustomerAtom'; import { CustomerInterface, CustomerResponseInterface } from '@interfaces/ResponseInterfaces/Customer/CustomerResponseInterface'; import { CustomerDniTypeInterface, CustomerDniTypeResponseInterface } from '@interfaces/ResponseInterfaces/Customer/DNITypeResponseInterface'; import { GetCustomer } from '@package/config/api/Customer/Customer/GetCustomer/page'; @@ -17,7 +15,8 @@ export const useClientSearchLogic = () => { idNumber: '' }); - const setCustomer = useSetAtom(CustomerAtom); + // Cliente seleccionado + const [customer, setCustomer] = useState<CustomerInterface | null>(null); const fetchIdTypes = async () => { setLoading(true); @@ -91,6 +90,7 @@ export const useClientSearchLogic = () => { formData, handleInputChange, handleSubmitForm, - isAffiliated + isAffiliated, + customer }; }; diff --git a/package/interfaces/OutputInterfaces/Sale/GetSaleOutputInterface.tsx b/package/interfaces/OutputInterfaces/Sale/GetSaleOutputInterface.tsx index d0bcd55..0462959 100644 --- a/package/interfaces/OutputInterfaces/Sale/GetSaleOutputInterface.tsx +++ b/package/interfaces/OutputInterfaces/Sale/GetSaleOutputInterface.tsx @@ -1,7 +1,7 @@ export interface GetSaleOutputInterface { idTransaction?: string; - storeNumber?: number; - idSubsidiary?: string; + storeNumber?: string; + idSubsidiary?: number; idUser?: string; pos?: string; } \ No newline at end of file diff --git a/package/interfaces/ResponseInterfaces/Sale/GetSaleResponseInterface.tsx b/package/interfaces/ResponseInterfaces/Sale/GetSaleResponseInterface.tsx index 82c0e29..d1356f3 100644 --- a/package/interfaces/ResponseInterfaces/Sale/GetSaleResponseInterface.tsx +++ b/package/interfaces/ResponseInterfaces/Sale/GetSaleResponseInterface.tsx @@ -1,5 +1,5 @@ -export interface Tax { +export interface TaxInteface { id: string; idSaleDetail: string; idProduct: string; @@ -13,7 +13,7 @@ export interface Tax { taxIncluded: boolean; } -export interface SaleDetail { +export interface SaleDetailInterface { idDetail: string; idTransaction: string; id: string; @@ -35,19 +35,19 @@ export interface SaleDetail { idUser: string; creation: string; lastUpdate: string; - taxes: Tax[]; + taxes: TaxInteface[]; active: boolean; weighable: boolean; extendedPrice: number; } -export interface CustomerPromotion { +export interface CustomerPromotionInterface { groupId: string; customerId: string; percentageDiscount: number | null; } -export interface ItemPromotion { +export interface ItemPromotionInterface { index: number; code: string; quantity: number; @@ -58,14 +58,14 @@ export interface ItemPromotion { weighable: boolean; } -export interface Promotions { - customer: CustomerPromotion; - items: ItemPromotion[]; +export interface PromotionsInterface { + customer: CustomerPromotionInterface; + items: ItemPromotionInterface[]; total: number; - listPromotion: any[]; // Assuming this is an empty array, can adjust if more data is provided + listPromotion: any[]; } -export interface SaleTransactionObject { +export interface SaleTransactionObjectInterface { id: string; idCompany: string; idSubsidiary: number; @@ -95,13 +95,13 @@ export interface SaleTransactionObject { localDateTime: string | null; creation: string; lastUpdate: string; - list: SaleDetail[]; - promotions: Promotions; + list: SaleDetailInterface[]; + promotions: PromotionsInterface; } export interface GetSaleResponseInterface { correct: boolean; message: string | null; errorCode: number; - object: SaleTransactionObject; + object: SaleTransactionObjectInterface; } diff --git a/package/interfaces/ResponseInterfaces/Sale/StartSaleResponseInterface.tsx b/package/interfaces/ResponseInterfaces/Sale/StartSaleResponseInterface.tsx index 312d7ce..a599f2a 100644 --- a/package/interfaces/ResponseInterfaces/Sale/StartSaleResponseInterface.tsx +++ b/package/interfaces/ResponseInterfaces/Sale/StartSaleResponseInterface.tsx @@ -7,5 +7,5 @@ export interface StartSaleResponseInterface { correct: boolean; message: string; errorCode: number; - object: TransactionObject; + object: TransactionObject | null; } \ No newline at end of file