diff --git a/package-lock.json b/package-lock.json index 5b8710a..a74cef6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "invoice", + "name": "invoice_and_zakat_tracking_system", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "invoice", + "name": "invoice_and_zakat_tracking_system", "version": "0.1.0", "dependencies": { "@emotion/react": "^11.8.2", @@ -16,6 +16,7 @@ "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", + "axios": "^1.5.1", "buffer": "^6.0.3", "firebase": "^9.6.8", "react": "^17.0.2", @@ -5601,6 +5602,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -7467,9 +7491,9 @@ } }, "node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "funding": [ { "type": "github", @@ -8829,9 +8853,9 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==" }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", @@ -14575,6 +14599,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -21912,6 +21941,28 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz", "integrity": "sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw==" }, + "axios": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -23317,9 +23368,9 @@ } }, "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" }, "domexception": { "version": "2.0.1", @@ -24335,9 +24386,9 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==" }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" }, "fork-ts-checker-webpack-plugin": { "version": "6.5.0", @@ -28374,6 +28425,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", diff --git a/package.json b/package.json index 813899d..f7cfddd 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", + "axios": "^1.5.1", "buffer": "^6.0.3", "firebase": "^9.6.8", "react": "^17.0.2", @@ -52,4 +53,3 @@ ] } } - diff --git a/src/components/CalculateZakat.jsx b/src/components/CalculateZakat.jsx new file mode 100644 index 0000000..2d821a9 --- /dev/null +++ b/src/components/CalculateZakat.jsx @@ -0,0 +1,121 @@ +import React, { useState } from 'react' +import Popup from './Popup' +import { Box, Typography } from '@mui/material' +import { getYearRange } from '../utils/helpers' +import { formatMoney } from '../reducer' +import PaidOutlinedIcon from '@mui/icons-material/PaidOutlined' +import CurrencyExchangeOutlinedIcon from '@mui/icons-material/CurrencyExchangeOutlined' +import { useEffect } from 'react' +import { getCurrentNisaabRate } from '../firebase/zakatyears' + +function CalculateZakat({ zakatYear, netAssets }) { + const [openPopup, setOpen] = useState(false) + const [currentNisaab, setCurrentNisaab] = useState(zakatYear?.nisab || 0) + const [expenses, setExpenses] = useState(0) + const [amountDue, setAmountDue] = useState((1 / 40) * netAssets) + + useEffect(() => { + getCurrentNisaabRate().then(setCurrentNisaab) + }, []) + useEffect(() => { + const assets = netAssets - expenses + if (assets >= currentNisaab) { + setAmountDue((1 / 40) * assets) + } else { + setAmountDue(0) + } + }, [expenses, currentNisaab, netAssets]) + + const handleSubmit = async (e) => { + e.preventDefault() + } + + return ( +
+ +
+ +
+ + +
+
+ + +
+
+ + setExpenses(e.target.valueAsNumber)} + /> +
+
+ + + + + Amount Due: + + + + {amountDue ? formatMoney(amountDue) : 'N/A'} + + + + {' '} + + +
+
+ +
+ ) +} + +export default CalculateZakat diff --git a/src/components/ProtectedRoute.jsx b/src/components/ProtectedRoute.jsx index 9e120ea..6ab989a 100644 --- a/src/components/ProtectedRoute.jsx +++ b/src/components/ProtectedRoute.jsx @@ -12,8 +12,9 @@ function ProtectedRoute({ children, restrictedTo = [], redirect = '/' }) { const [loggedUser, setLoggedUser] = useState(auth.currentUser) useEffect(() => { + let isCanceled = false const unsubscribe = userChanged((user) => { - if (user) { + if (user && !isCanceled) { setLoggedUser(user) if (!user) getOne('users', loggedUser.id) @@ -22,7 +23,10 @@ function ProtectedRoute({ children, restrictedTo = [], redirect = '/' }) { } }) - return () => unsubscribe() + return () => { + isCanceled = true + unsubscribe() + } }, [loggedUser, user, dispatch]) if (!loggedUser) { diff --git a/src/css/Reports.css b/src/css/Reports.css index d9b41fc..ec3ea07 100644 --- a/src/css/Reports.css +++ b/src/css/Reports.css @@ -21,7 +21,7 @@ } section.left > div { - margin: 1rem 0; + /* margin: 1rem 0; */ display: flex; flex-direction: column; gap: 15px; @@ -74,6 +74,7 @@ section.left .filter_container .input { } .reports.container.dashboard { flex-direction: row; + gap: 1.5rem; } .reports > section.right .chart { padding: 10px; diff --git a/src/devdata/data.js b/src/devdata/data.js index 1665b51..854a340 100644 --- a/src/devdata/data.js +++ b/src/devdata/data.js @@ -29,7 +29,7 @@ export const products = temp.map((name) => { export const zakatYears = [ { id: uid(6), - openingBalance: Math.random() * 1_500_000, + openingBalance: 1_500_000 - Math.random() * 700_000, closingBalance: null, nisab: 700_000 + Math.random() * 100_000, beginDate: new Date(Date.now() - oneMonth), @@ -42,7 +42,7 @@ export const zakatYears = [ }, { id: uid(6), - openingBalance: Math.random() * 1_500_000, + openingBalance: 1_500_000 - Math.random() * 700_000, nisab: 700_000 + Math.random() * 100_000, beginDate: new Date(Date.now() - 13 * oneMonth), endDate: new Date(Date.now() - oneMonth), @@ -56,53 +56,55 @@ export const zakatYears = [ ] // Dummy RInvoices -const data = [] -for (let i = 0; i < 15; i++) { - let invoiceTotal = 0 - data.push({ - id: uid(6), - billerStreetAddress: 'Street Address' + (i + 1), - billerCity: 'City' + (i + 1), - billerZipCode: 'Zip Code' + (i + 1), - billerCountry: 'Country' + (i + 1), - clientName: 'Name' + (i + 1), - clientPhone: Math.floor(Math.random() * 100_000_000_000), - clientEmail: 'email' + (i + 1) + '@example.com', - clientAddress: 'strret address' + (i + 1), - clientCity: 'client City' + (i + 1), - clientZipCode: 'zip code' + (i + 1), - clientCountry: 'client country ' + (i + 1), - invoiceDateUnix: '', - invoiceDate: new Date( - 2022, - Math.round(Math.random() * 12), - Math.round(Math.random() * 25) - ), - paymentTerms: 'some terms', - paymentDueDate: new Date(), - paymentDueDateUnix: new Date(Date.now() + (i + 1) * 86400), - productDescription: '', - invoicePending: !!(i % 2), - invoiceDraft: '', - invoicePaid: !(i % 2), - invoiceItemList: Array(i + 1) - .fill() - .map((_, i) => { - const engine = products[Math.floor(Math.random() * products.length)] - invoiceTotal += engine.basePrice * (i + 1) - return { - itemName: engine.name, - engineNo: Math.floor(Math.random() * 10000000), - qty: i + 1, - price: engine.basePrice, - cost: engine.costPrice, - total: engine.basePrice * (i + 1), - } - }), - invoiceTotal, - userID: i % users.length, +const data = Array(15) + .fill(null) + .map((_, i) => { + let invoiceTotal = 0 + return { + id: uid(6), + billerStreetAddress: 'Street Address' + (i + 1), + billerCity: 'City' + (i + 1), + billerZipCode: 'Zip Code' + (i + 1), + billerCountry: 'Country' + (i + 1), + clientName: 'Name' + (i + 1), + clientPhone: Math.floor(Math.random() * 100_000_000_000), + clientEmail: 'email' + (i + 1) + '@example.com', + clientAddress: 'strret address' + (i + 1), + clientCity: 'client City' + (i + 1), + clientZipCode: 'zip code' + (i + 1), + clientCountry: 'client country ' + (i + 1), + invoiceDateUnix: '', + invoiceDate: new Date( + i < 8 ? 2022 : 2023, + Math.round(Math.random() * 12), + Math.round(Math.random() * 25) + ), + paymentTerms: 'some terms', + paymentDueDate: new Date(), + paymentDueDateUnix: new Date(Date.now() + (i + 1) * 86400), + productDescription: '', + invoicePending: !!(i % 2), + invoiceDraft: '', + invoicePaid: !(i % 2), + invoiceItemList: Array(i + 1) + .fill() + .map((_, i) => { + const engine = products[Math.floor(Math.random() * products.length)] + invoiceTotal += engine.basePrice * (i + 1) + return { + itemName: engine.name, + engineNo: Math.floor(Math.random() * 10000000), + qty: i + 1, + price: engine.basePrice, + cost: engine.costPrice, + total: engine.basePrice * (i + 1), + } + }), + invoiceTotal, + userID: i % users.length, + zakatYearID: zakatYears[i < 8 ? 1 : 0].id, + } }) -} // Dummy chart data export const userSalesData = [ diff --git a/src/devdata/temp.json b/src/devdata/temp.json deleted file mode 100644 index 1074432..0000000 --- a/src/devdata/temp.json +++ /dev/null @@ -1,564 +0,0 @@ -[ - { - "revenueService": { - "revenueServiceId": "079c5c0c-40e7-4694-b5d2-a26580517623", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": null, - "amount": 1000.0, - "categoryName": "Application Form Fee", - "categoryDescription": "Form Fee", - "categoryStatus": null, - "accountNo": "010", - "accountCode": "010", - "duration": "{\"years\":1,\"months\":1,\"days\":1}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-07T17:34:41.662+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2023-05-21T01:55:46.526+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": null, - "parentName": null, - "serviceAbbreviation": "AF", - "requireInvoiceNo": false, - "applicationId": null, - "signatureId": "3e7ed51d-76ab-4839-a090-fd1305061587", - "autoApproval": false, - "manualInvoice": true - }, - "children": [ - { - "revenueService": { - "revenueServiceId": "d08e1fcc-1af0-4404-a26e-3e83e6410d99", - "revenueServiceCode": "4430731", - "parentServiceId": "079c5c0c-40e7-4694-b5d2-a26580517623", - "invoiceStructureId": "2549725e-a4c8-43e7-890e-1fafe13ed784", - "amount": 0.0, - "categoryName": "Test Fee", - "categoryDescription": "Test Fee", - "categoryStatus": null, - "accountNo": "090000", - "accountCode": "0909999", - "duration": "{\"years\":0,\"months\":0,\"days\":90}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-05-09T20:39:23.092+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-05-27T19:01:19.993+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Numbering Renewal for Fixed & Mobile", - "parentName": "Application Form Fee", - "serviceAbbreviation": "TF", - "requireInvoiceNo": true, - "applicationId": null, - "signatureId": "7d7c6c84-4f43-46bf-bde9-503f4ca8ad88", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - } - ] - }, - { - "revenueService": { - "revenueServiceId": "77065666-c075-4a65-a8fd-97bd80e21ebc", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": "af99b9e0-c525-4420-90c5-91a4da92311e", - "amount": 2000.0, - "categoryName": "Short Code", - "categoryDescription": "Short Code", - "categoryStatus": null, - "accountNo": "-", - "accountCode": "-", - "duration": "{\"years\":1,\"months\":0,\"days\":0}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-07-13T13:06:17.879+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-07-25T08:30:29.436+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Numbering Short Code", - "parentName": null, - "serviceAbbreviation": "SC", - "requireInvoiceNo": false, - "applicationId": "spectrum", - "signatureId": "e82356cf-decf-45d3-a73f-cd7c0e471051", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "367703a7-2db8-4e35-b600-211e629af69e", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": "9ae23c9b-dc93-40ac-9f41-68438c61d5f0", - "amount": 0.0, - "categoryName": "Type Approval", - "categoryDescription": "Type Approval", - "categoryStatus": null, - "accountNo": "009", - "accountCode": "009", - "duration": "{\"years\":1,\"months\":1,\"days\":1}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-07T17:33:27.258+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2023-02-01T22:36:12.667+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Type Approval", - "parentName": null, - "serviceAbbreviation": "TY", - "requireInvoiceNo": true, - "applicationId": "d8a36768-47a3-40e5-a95a-8af51a4e4bcc", - "signatureId": "7d7c6c84-4f43-46bf-bde9-503f4ca8ad88", - "autoApproval": true, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "7e4c538b-e94e-4fa2-b4e7-971e283ae308", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": "79c0c1c0-b7e3-4a64-87fd-935758639c27", - "amount": 1999.0, - "categoryName": "Another Service", - "categoryDescription": "Just Another Service", - "categoryStatus": null, - "accountNo": "03040", - "accountCode": "03040", - "duration": "{\"years\":0,\"months\":0,\"days\":0}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-05-31T09:08:38.767+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2023-05-23T08:13:30.110+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Meck Test Structure", - "parentName": null, - "serviceAbbreviation": "AS", - "requireInvoiceNo": false, - "applicationId": null, - "signatureId": "e82356cf-decf-45d3-a73f-cd7c0e471051", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "a65ce2f8-d9db-434f-a7f9-af8590fe9608", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": null, - "amount": 0.0, - "categoryName": "Numbering Plan Fee", - "categoryDescription": "Numbering Plan", - "categoryStatus": null, - "accountNo": "090", - "accountCode": "090", - "duration": "{\"years\":null,\"months\":12,\"days\":0}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-07T17:35:43.316+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-05-25T13:18:27.489+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": null, - "parentName": null, - "serviceAbbreviation": "NP", - "requireInvoiceNo": true, - "applicationId": null, - "signatureId": "7d7c6c84-4f43-46bf-bde9-503f4ca8ad88", - "autoApproval": false, - "manualInvoice": true - }, - "children": [ - { - "revenueService": { - "revenueServiceId": "93a3aec6-42f8-43c0-8832-de61bbce2209", - "revenueServiceCode": "4430731", - "parentServiceId": "a65ce2f8-d9db-434f-a7f9-af8590fe9608", - "invoiceStructureId": "8f93b60a-104e-4d17-a6bc-9c9fe7d4961b", - "amount": 0.0, - "categoryName": "Numbering Plan New - Fixed&Mobile", - "categoryDescription": "Numbering Plan New - Fixed&Mobile", - "categoryStatus": null, - "accountNo": "8910", - "accountCode": "891", - "duration": "{\"years\":0,\"months\":0,\"days\":0}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-10T14:07:53.267+0000", - "createdBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-04-20T11:59:44.713+0000", - "modifiedBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Numbering New Invoice for Fixed & Mobile", - "parentName": "Numbering Plan Fee", - "serviceAbbreviation": "NP", - "requireInvoiceNo": true, - "applicationId": null, - "signatureId": "79f3402d-df5f-4843-bba9-c90b8b6c9ac2", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "b17ad0da-11ed-4823-b73b-ef27fc749e90", - "revenueServiceCode": "4430731", - "parentServiceId": "a65ce2f8-d9db-434f-a7f9-af8590fe9608", - "invoiceStructureId": "2549725e-a4c8-43e7-890e-1fafe13ed784", - "amount": 0.0, - "categoryName": "Numbering Plan Renewal - Fixed&Mobile", - "categoryDescription": "Numbering Renewal - Fixed&Mobile", - "categoryStatus": null, - "accountNo": "8902", - "accountCode": "8902", - "duration": "{\"years\":0,\"months\":0,\"days\":0}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-10T14:10:06.066+0000", - "createdBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2023-01-27T15:06:23.833+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Numbering Renewal for Fixed & Mobile", - "parentName": "Numbering Plan Fee", - "serviceAbbreviation": "NP", - "requireInvoiceNo": true, - "applicationId": null, - "signatureId": "79f3402d-df5f-4843-bba9-c90b8b6c9ac2", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - } - ] - }, - { - "revenueService": { - "revenueServiceId": "361b4cfc-1ef3-42a0-b057-a45392f3a78e", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": "313023d4-f469-472b-8ab3-21109b2cd4f0", - "amount": 0.0, - "categoryName": "License Fees", - "categoryDescription": "License Fees", - "categoryStatus": null, - "accountNo": "048302", - "accountCode": "05939", - "duration": "{\"years\":0,\"months\":0,\"days\":0}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-05-31T14:09:02.264+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-05-31T14:10:26.262+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "License Structure", - "parentName": null, - "serviceAbbreviation": "LF", - "requireInvoiceNo": true, - "applicationId": "licensing", - "signatureId": "2af2e945-50f9-47a9-91e6-9afb578e38f2", - "autoApproval": true, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "a2992ee9-6e82-4191-b792-d45116a690c2", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": null, - "amount": 0.0, - "categoryName": "Spectrum Fee", - "categoryDescription": "Spectrum Fee", - "categoryStatus": null, - "accountNo": "890087", - "accountCode": "890008", - "duration": "{\"years\":0,\"months\":0,\"days\":0}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-10T14:36:01.133+0000", - "createdBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": null, - "modifiedBy": null, - "invoiceStructureName": null, - "parentName": null, - "serviceAbbreviation": "SP", - "requireInvoiceNo": true, - "applicationId": null, - "signatureId": null, - "autoApproval": false, - "manualInvoice": true - }, - "children": [ - { - "revenueService": { - "revenueServiceId": "007fe7c7-e666-4989-9b42-4d5da8f941e0", - "revenueServiceCode": "4430731", - "parentServiceId": "a2992ee9-6e82-4191-b792-d45116a690c2", - "invoiceStructureId": "3fc5ce78-217d-41e9-9477-4e1234d68e52", - "amount": 0.0, - "categoryName": "Microwave & 7080 eBand Spectrum", - "categoryDescription": "Microwave & 7080 eBand Spectrum", - "categoryStatus": null, - "accountNo": "89007", - "accountCode": "890002", - "duration": "{\"years\":0,\"months\":0,\"days\":60}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-10T14:39:44.912+0000", - "createdBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-08-01T14:06:57.157+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Spectrum - Microwave & 7080 eBand", - "parentName": "Spectrum Fee", - "serviceAbbreviation": "SP", - "requireInvoiceNo": true, - "applicationId": "spectrum", - "signatureId": "7d7c6c84-4f43-46bf-bde9-503f4ca8ad88", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "005e46f9-59fe-4797-8c38-8e398b71ca71", - "revenueServiceCode": "4430731", - "parentServiceId": "a2992ee9-6e82-4191-b792-d45116a690c2", - "invoiceStructureId": "f2ac4c7e-359c-426a-988a-880101a48911", - "amount": 0.0, - "categoryName": "Space Service Frequency", - "categoryDescription": "Space Service Frequency", - "categoryStatus": null, - "accountNo": "89007", - "accountCode": "89008", - "duration": "{\"years\":0,\"months\":0,\"days\":0}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-10T14:37:22.480+0000", - "createdBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-07-18T14:33:21.906+0000", - "modifiedBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Spectrum - ESIM Space", - "parentName": "Spectrum Fee", - "serviceAbbreviation": "SP", - "requireInvoiceNo": true, - "applicationId": null, - "signatureId": "e2fa06cf-0e8f-4156-9e06-1d05e6227a11", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "7b6b09be-6d62-4ad7-83dc-de1cff3cf28b", - "revenueServiceCode": "4430731", - "parentServiceId": "a2992ee9-6e82-4191-b792-d45116a690c2", - "invoiceStructureId": "011a6512-1c3e-488f-bd29-55c81463d53d", - "amount": 0.0, - "categoryName": "WLL Spectrum", - "categoryDescription": "WLL Spectrum", - "categoryStatus": null, - "accountNo": "4430731", - "accountCode": "4430731", - "duration": "{\"years\":0,\"months\":0,\"days\":60}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-07-18T14:36:38.280+0000", - "createdBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-08-01T14:07:57.755+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "WLL Spectrum", - "parentName": "Spectrum Fee", - "serviceAbbreviation": "SP", - "requireInvoiceNo": true, - "applicationId": "spectrum", - "signatureId": "e2fa06cf-0e8f-4156-9e06-1d05e6227a11", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "e1e9e90f-7051-493f-aef9-86fbebb366cc", - "revenueServiceCode": "4430731", - "parentServiceId": "a2992ee9-6e82-4191-b792-d45116a690c2", - "invoiceStructureId": "c9c6d934-7119-4c2e-b1e5-778f98e7c467", - "amount": 0.0, - "categoryName": "SPECIAL INVOICE FOR SPECTRUM TRADING, AUCTION, ETC", - "categoryDescription": "SPECIAL INVOICE FOR SPECTRUM TRADING, AUCTION", - "categoryStatus": null, - "accountNo": "4430731", - "accountCode": "4430731", - "duration": "{\"years\":0,\"months\":0,\"days\":60}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-08-16T09:31:22.028+0000", - "createdBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-08-16T09:35:56.902+0000", - "modifiedBy": "{\"appUserId\":\"fff022c9-5490-41e7-9cd4-9e22a247405b\",\"username\":\"finance_kiames\",\"name\":\"James Akinniranye\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": "Special Spectrum Invoice Structure", - "parentName": "Spectrum Fee", - "serviceAbbreviation": "SP", - "requireInvoiceNo": true, - "applicationId": null, - "signatureId": "79f3402d-df5f-4843-bba9-c90b8b6c9ac2", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - } - ] - }, - { - "revenueService": { - "revenueServiceId": "efe3f77d-8e48-44f4-acde-17ac25d55278", - "revenueServiceCode": "e", - "parentServiceId": null, - "invoiceStructureId": "eab88166-481d-49c8-86c4-300a4400ce81", - "amount": 0.0, - "categoryName": "te", - "categoryDescription": "te", - "categoryStatus": null, - "accountNo": "etecec", - "accountCode": "et", - "duration": "{\"years\":0,\"months\":0,\"days\":100}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-11T05:34:44.584+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": null, - "modifiedBy": null, - "invoiceStructureName": "te", - "parentName": null, - "serviceAbbreviation": "te", - "requireInvoiceNo": false, - "applicationId": null, - "signatureId": null, - "autoApproval": false, - "manualInvoice": true - }, - "children": [ - { - "revenueService": { - "revenueServiceId": "0ecfc03e-61d1-4464-8c09-1daf862b1403", - "revenueServiceCode": "432", - "parentServiceId": "efe3f77d-8e48-44f4-acde-17ac25d55278", - "invoiceStructureId": null, - "amount": 0.0, - "categoryName": "remote", - "categoryDescription": "desc", - "categoryStatus": null, - "accountNo": "444", - "accountCode": "000", - "duration": "{\"years\":1,\"months\":0,\"days\":0}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-04-11T13:18:41.044+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": "2022-04-11T13:19:25.709+0000", - "modifiedBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "invoiceStructureName": null, - "parentName": "te", - "serviceAbbreviation": "REM", - "requireInvoiceNo": false, - "applicationId": "1721f893-b6de-4989-9bd7-9387c884f6c6", - "signatureId": "3e7ed51d-76ab-4839-a090-fd1305061587", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - } - ] - }, - { - "revenueService": { - "revenueServiceId": "cfa262db-aca7-4b32-ba9c-5b0b62e16dfa", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": null, - "amount": 0.0, - "categoryName": "Revalidation Fee", - "categoryDescription": "Payment For Revalidation", - "categoryStatus": null, - "accountNo": "509960", - "accountCode": "594095", - "duration": "{\"years\":0,\"months\":0,\"days\":0}", - "allowPartPayment": true, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2022-06-14T12:07:17.992+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": null, - "modifiedBy": null, - "invoiceStructureName": null, - "parentName": null, - "serviceAbbreviation": "RF", - "requireInvoiceNo": false, - "applicationId": "licensing", - "signatureId": "", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - }, - { - "revenueService": { - "revenueServiceId": "da4c4010-afab-4016-917f-8f0696ef3fe8", - "revenueServiceCode": "4430731", - "parentServiceId": null, - "invoiceStructureId": null, - "amount": 10000.0, - "categoryName": "Tender Fee", - "categoryDescription": "Tender Fee", - "categoryStatus": null, - "accountNo": "010", - "accountCode": "010", - "duration": "{\"years\":1,\"months\":1,\"days\":1}", - "allowPartPayment": false, - "actionAdditionalDetails": null, - "deleted": false, - "dateCreated": "2023-07-07T02:34:41.662+0000", - "createdBy": "{\"appUserId\":\"cf7cdab4-77c7-4223-917d-cbc6cb20b569\",\"username\":\"finance-admin\",\"name\":\"Finance Admin\",\"organizationId\":\"70b3fbd7-9281-4f7f-9ba2-5c439ae993ce\",\"organizationName\":\"Finance Department\"}", - "dateModified": null, - "modifiedBy": null, - "invoiceStructureName": null, - "parentName": null, - "serviceAbbreviation": "TF", - "requireInvoiceNo": false, - "applicationId": null, - "signatureId": "1d4eea8a-a472-49b0-a81e", - "autoApproval": false, - "manualInvoice": true - }, - "children": [] - } -] diff --git a/src/firebase/crud.js b/src/firebase/crud.js index 0ae7b72..0afd528 100644 --- a/src/firebase/crud.js +++ b/src/firebase/crud.js @@ -12,13 +12,13 @@ import { uid } from 'uid' import { db, devEnv } from './_config' /** - * This file Defgines the crud operations for collections + * This file Defines the crud operations for collections */ /** * Retrieve all documents from a collection * @param {"invoices" | "users" | "products" | "zakatyears"} colname - * @returns + * @returns {Promise} */ export const getAll = async (colname) => { if (devEnv) { diff --git a/src/firebase/factory.js b/src/firebase/factory.js index f842720..57d5eea 100644 --- a/src/firebase/factory.js +++ b/src/firebase/factory.js @@ -1,3 +1,6 @@ +import Invoices from '../devdata/data' +import { cleanDate } from '../utils/dateFunctions' + /** * gets the monthname of a date * @param {Date} dt @@ -45,22 +48,19 @@ const groupBy = (data, key) => { /** * unwind Invoices by there itemList - * @param {[Object]} invoices List of invoices + * @param {(typeof Invoices)} invoices List of invoices * @returns {[Object]} list of unwind invoice */ export const transformInvoices = (invoices) => { const transformed = [] + invoices.forEach((inv) => { inv.invoiceItemList.forEach((item, i) => { transformed.push({ id: inv.id + i, userID: inv.userID, - date: inv.invoiceDate?.hasOwnProperty('seconds') - ? new Date( - inv.invoiceDate?.seconds * 1000 + - inv.invoiceDate?.nanoseconds / 1000000 - ) - : new Date(inv.invoiceDate), + zakatYearID: inv.zakatYearID, + date: cleanDate(inv.invoiceDate), ...item, }) }) diff --git a/src/firebase/products.js b/src/firebase/products.js index bb77879..cefe908 100644 --- a/src/firebase/products.js +++ b/src/firebase/products.js @@ -8,6 +8,7 @@ import { } from 'firebase/firestore/lite' import { products } from '../devdata/data' import { db, devEnv } from './_config' +import { getAll } from './crud' /** * This file defines specific functions related to products collection @@ -54,3 +55,16 @@ export const updateProductQuantities = async (items) => { }) await Promise.all(updates) } + +/** + * Returns the Current Assets Value in Stock + * @returns {Promise} + */ +export const getCurrentAssetsValue = async () => { + const products = await getAll('products') + + return products.reduce( + (subtotal, p) => subtotal + p.costPrice * p.quantity, + 0 + ) +} diff --git a/src/firebase/zakatyears.js b/src/firebase/zakatyears.js index 40eb2bf..7ebb6cf 100644 --- a/src/firebase/zakatyears.js +++ b/src/firebase/zakatyears.js @@ -1,10 +1,14 @@ import { collection, getDocs, query, where } from 'firebase/firestore/lite' -import { db, devEnv } from './_config' +import { db, devEnv, goldApiDotIoApiKey } from './_config' import { getAll } from './crud' +import { ZakatYear, Invoice } from '../models' +import { getCurrentAssetsValue } from './products' +import axios from 'axios' +import { convertNairaToUsd, convertUsdToNaira } from '../utils/helpers' /** * Get The Active Zakata Year - * @returns + * @returns {ZakatYear} */ export const getActiveYear = async () => { if (devEnv) { @@ -22,3 +26,95 @@ export const getActiveYear = async () => { ) return years.length ? years[0] : null } + +/** + * Retrieve All invoices in particular zaka + * @param {string} id zakat year id + * @returns {Promise} + */ +export const getZakatYearsInvoices = async (id) => { + let invoices = [] + + if (devEnv) { + invoices = (await getAll('invoices')).filter( + (inv) => inv.zakatYearID === id + ) + } else { + const q = query(collection(db, 'invoices'), where('zakatYearID', '==', id)) + const docsSnapshot = await getDocs(q) + docsSnapshot.forEach((doc) => + invoices.push({ + ...doc.data(), + id: doc.id, + }) + ) + } + + return invoices +} + +/** + * Gets the Total Sales falling within the specified Year + * @param {string} id + * @returns {Promise} Total Sales within the Year + */ +export const getZakatYearsSales = async (id) => { + const invoices = await getZakatYearsInvoices(id) + const sale = invoices.reduce( + (subtotal, inv) => subtotal + inv.invoiceTotal, + 0 + ) + return sale +} + +/** + * Give an Estimate of amount Due for a given Zakat Year + * @param {ZakatYear} year The Year to calculate for + * @returns {Promise} Estimated Amount + */ +export const getEstimatedAmountDue = async (year) => { + let sales = await getZakatYearsSales(year.id) + + // Estimated 25% of Sales lost due to miscalleneous + sales *= 0.75 + + // Remove Opening Balance of The Year + sales -= year.openingBalance + + // Add The Current Value of assets in stock + sales += await getCurrentAssetsValue() + + // Calculate Legal (1/40) + const due = Math.ceil(sales * (1 / 40)) + + return due +} + +/** + * Returns The Current Nisaab in Naira + * @returns {Promise} + */ +export const getCurrentNisaabRate = async () => { + if (devEnv) { + const rand = Math.random() * 100_000 + const det = Math.random() + return 4_000_000 + rand * (det < 0.5 ? -1 : 1) + } + const response = await axios({ + url: 'https://www.goldapi.io/api/XAU/USD', + method: 'get', + + headers: { + 'x-access-token': goldApiDotIoApiKey, + 'Content-Type': 'application/json', + }, + }) + // Get Price In Ounce + const oneOunceUsdPrice = response.data.price + + // Nisaab of Gold is 2.73295 ounces + const nisaabInUsd = 2.73295 * oneOunceUsdPrice + const nisaabInNaira = await convertUsdToNaira(nisaabInUsd) + + return nisaabInNaira +} diff --git a/src/index.css b/src/index.css index 5bb74f1..e2b3a2d 100644 --- a/src/index.css +++ b/src/index.css @@ -79,11 +79,15 @@ button, flex-direction: column; } +.jc-space-between { + justify-content: space-between; +} + .container { width: 100%; padding: 0; padding-bottom: 1rem; - max-width: 1200px; + max-width: 1000px; max-height: 100vh; overflow-y: scroll; margin: 0 auto; diff --git a/src/models/index.js b/src/models/index.js index 531537c..f8b879e 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -22,3 +22,73 @@ export const Invoice = { userID: null, zakatYearID: null, } + +/** + * Zakat year default model + */ +export const ZakatYear = { + id: '', + /** + * Net Asset Value as the begining of The Year + * must be >= nisaab + */ + openingBalance: 0, + /** + * The Nisaab as at the begining of the Year + */ + nisab: 0, + /** + * Start Date of the Year range + */ + beginDate: new Date(), + /** + * End Date of the Year range + */ + endDate: new Date(), + /** + * Date The Due amount is paid (if applicable) + */ + datePaid: new Date(), + /** + * Net Assets Value as at the End of the Year Range + */ + closingBalance: 0, + /** + * Amount Due for The Year (to be calculated at the end of the Year) + */ + amountDue: 0, + /** + * Marker for the Years time + * (active for current inactive otherwise) + */ + status: 'inactive', + /** + * Marker for Payment + * one of ('paid', 'not-paid', 'not-applicable') + */ + paymentStatus: 'not-paid', + dateCreated: new Date(), +} + +/** + * + */ +export const Product = { + /** + * Name of The Product + * it has be unique + */ + name: '', + /** + * Cost of The Product to the Business owner + */ + costPrice: 0, + /** + * Quantity of the Product in stock + */ + quantity: 0, + /** + * Borderline selling price of the product + */ + basePrice: 0, +} diff --git a/src/pages/Zakat.jsx b/src/pages/Zakat.jsx index d8714fe..3f58c00 100644 --- a/src/pages/Zakat.jsx +++ b/src/pages/Zakat.jsx @@ -15,7 +15,14 @@ import { useStateValue } from '../StateProvider' import { cleanDate, dayDifference, toDdMmmYy } from '../utils/dateFunctions' import useTable from '../hooks/useTable' import { getAll } from '../firebase/crud' -import { toNDigits } from '../utils/helpers' +import { getYearRange, toNDigits } from '../utils/helpers' +import { + getEstimatedAmountDue, + getZakatYearsSales, +} from '../firebase/zakatyears' +import { ZakatYear } from '../models' +import { getCurrentAssetsValue } from '../firebase/products' +import CalculateZakat from '../components/CalculateZakat' const headCells = [ { id: 'year', label: 'Year' }, @@ -29,30 +36,31 @@ function Zakat() { const [loading, setLoading] = useState(false) const [zakatyears, setZakatyears] = useState([]) const [filter, setFilter] = useState({ fn: (items) => items }) + const [currentYear, setCurrentYear] = useState(ZakatYear) + const [yearSales, setYearSales] = useState(0) + const [yearDueEst, setYearDueEst] = useState(0) + const [assetInStock, setAssetsInStock] = useState(0) + const { activeYear } = useStateValue()[0] - const { activeYear: currentYear } = useStateValue()[0] const { TableContainer, TblHead, TblPagination, recordsAfterPagination } = useTable(zakatyears, headCells, filter) useEffect(() => { let isCanceled = false getAll('zakatyears').then((years) => !isCanceled && setZakatyears(years)) + getCurrentAssetsValue().then( + (value) => !isCanceled && setAssetsInStock(value) + ) + setCurrentYear(activeYear) return () => { isCanceled = true } - }, []) + }, [activeYear]) - const getYear = (year) => { - const start = cleanDate(year.beginDate) - const stop = cleanDate(year.endDate) - return `${toNDigits( - start.getMonth() + 1, - 2 - )}/${start.getFullYear()} - ${toNDigits( - stop.getMonth() + 1, - 2 - )}/${stop.getFullYear()}` - } + useEffect(() => { + getZakatYearsSales(currentYear?.id).then(setYearSales) + getEstimatedAmountDue(currentYear).then(setYearDueEst) + }, [currentYear]) return (
@@ -61,37 +69,35 @@ function Zakat() { {/* Top Level Info */}

Zakat Year

-
+
- {currentYear.status} + {currentYear?.status}
- {currentYear.paymentStatus} + {currentYear?.paymentStatus}
-
+
- {toDdMmmYy(currentYear.beginDate)}
- {toDdMmmYy(currentYear.endDate)}
@@ -103,7 +109,7 @@ function Zakat() { Opening Balance - {formatMoney(currentYear.openingBalance)} + {formatMoney(currentYear?.openingBalance)}
@@ -111,15 +117,23 @@ function Zakat() { Opening Nisaab - {formatMoney(currentYear.nisab)} + {formatMoney(currentYear?.nisab)} + +
+
+ + Year Sales + + + {formatMoney(yearSales)}
- Current Balance + Assets In Stock - {formatMoney(currentYear.openingBalance)} + {formatMoney(assetInStock)}
@@ -127,7 +141,7 @@ function Zakat() { Due in (Days) - {dayDifference(new Date(), currentYear.endDate)} + {dayDifference(new Date(), currentYear?.endDate)}
@@ -135,13 +149,17 @@ function Zakat() { Amount Due (Est.) - {currentYear.openingBalance >= currentYear.nisab - ? formatMoney(currentYear.averageMonthly) + {yearSales >= currentYear?.nisab + ? formatMoney(yearDueEst) : 'N/A'}
- + + {/* */}
{recordsAfterPagination().map((y, i) => ( - {getYear(y)} + {getYearRange(y)} {formatMoney(y.openingBalance)} {formatMoney(y.closingBalance)} {formatMoney(y.nisab)} diff --git a/src/utils/helpers.js b/src/utils/helpers.js index 13fc74c..5445084 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -1,4 +1,8 @@ // import { cleanDate } from "./dateFunctions" +import { XEApiID, XEApiKey, devEnv } from '../firebase/_config' +import { ZakatYear } from '../models' +import { cleanDate } from './dateFunctions' +import axios from 'axios' /** * @author MAHADI @@ -13,3 +17,46 @@ export const toNDigits = (value, digits) => { return String(value).padStart(digits, '0') } + +/** + * Gets The Year Range of a Zakat Year in the form + * mm/yyyy - mm/yyyy + * @param {ZakatYear} year + * @returns {string} Year Range + */ +export const getYearRange = (year) => { + if (!year) return '' + const start = cleanDate(year.beginDate) + const stop = cleanDate(year.endDate) + return `${toNDigits( + start.getMonth() + 1, + 2 + )}/${start.getFullYear()} - ${toNDigits( + stop.getMonth() + 1, + 2 + )}/${stop.getFullYear()}` +} + +/** + * Converts Usd To Naira Using the XE api + * @param {number} usd the amount of usd + * @returns {Promise} equivalent of usd in naira + */ +export const convertUsdToNaira = async (usd) => { + if (devEnv) { + return usd * 875 + } + const url = `https://xecdapi.xe.com/v1/convert_from.csv/?from=USD&to=NGN&amount=${usd}` + const response = await axios({ + url, + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + auth: { + username: XEApiID, + password: XEApiKey, + }, + }) + return Number(response.data.split(',').slice(-1)[0]) +}