diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..868fb3aa4 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,180 @@ +version: 2.1 +orbs: + #snyk: snyk/snyk@0.0.8 + #cypress: cypress-io/cypress@1.23.0 + aws-s3: circleci/aws-s3@2.0.0 + eb: circleci/aws-elastic-beanstalk@1.0.2 + jira: circleci/jira@1.3.1 +jobs: + api-deploy: + docker: + - image: "cimg/base:stable" + steps: + - checkout + - eb/setup + - run: + command: | + eb init imex-online-production-api -r ca-central-1 -p "Node.js 16 running on 64bit Amazon Linux 2" + eb status --verbose + eb deploy + eb status + - jira/notify + + hasura-migrate: + docker: + - image: cimg/node:16.15.0 + parameters: + secret: + type: string + default: $HASURA_PROD_SECRET + working_directory: ~/repo/hasura + steps: + - checkout: + path: ~/repo + - run: + name: Execute migration + command: | + npm install hasura-cli -g + hasura migrate apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >> + hasura metadata apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >> + hasura metadata reload --endpoint https://db.imex.online/ --admin-secret << parameters.secret >> + + app-build: + docker: + - image: cimg/node:16.15.0 + + working_directory: ~/repo/client + + steps: + - checkout: + path: ~/repo + + - restore_cache: + name: Restore Yarn Package Cache + keys: + - yarn-packages-{{ checksum "yarn.lock" }} + - run: + name: Install Dependencies + command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn + - save_cache: + name: Save Yarn Package Cache + key: yarn-packages-{{ checksum "yarn.lock" }} + paths: + - ~/.cache/yarn + + - run: yarn run build + + - aws-s3/sync: + from: build + to: "s3://imex-online-production/" + - jira/notify + + test-hasura-migrate: + docker: + - image: cimg/node:16.15.0 + parameters: + secret: + type: string + default: $HASURA_TEST_SECRET + working_directory: ~/repo/hasura + steps: + - checkout: + path: ~/repo + - run: + name: Execute migration + command: | + npm install hasura-cli -g + echo ${HASURA_TEST_SECRET} + hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >> + hasura metadata apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >> + hasura metadata reload --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >> + + test-app-build: + docker: + - image: cimg/node:16.15.0 + + working_directory: ~/repo/client + + steps: + - checkout: + path: ~/repo + + - restore_cache: + name: Restore Yarn Package Cache + keys: + - yarn-packages-{{ checksum "yarn.lock" }} + - run: + name: Install Dependencies + command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn + - save_cache: + name: Save Yarn Package Cache + key: yarn-packages-{{ checksum "yarn.lock" }} + paths: + - ~/.cache/yarn + + - run: yarn run build:test + + - aws-s3/sync: + from: build + to: "s3://imex-online-test/" + - jira/notify + + admin-app-build: + docker: + - image: cimg/node:16.15.0 + + working_directory: ~/repo/admin + + steps: + - checkout: + path: ~/repo + + - restore_cache: + keys: + - v1-dependencies-{{ checksum "package.json" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + - run: npm i + + - save_cache: + paths: + - node_modules + - ~/.npm + - ~/.cache + key: v1-dependencies-{{ checksum "package.json" }} + + - run: npm run build + + - aws-s3/sync: + from: build + to: "s3://adm.imex.online/" + +workflows: + deploy_and_build: + jobs: + - api-deploy: + filters: + branches: + only: master + - app-build: + filters: + branches: + only: master + - hasura-migrate: + secret: ${HASURA_PROD_SECRET} + filters: + branches: + only: master + - test-app-build: + filters: + branches: + only: test + - test-hasura-migrate: + secret: ${HASURA_TEST_SECRET} + filters: + branches: + only: test + #- admin-app-build: + #filters: + #branches: + #only: master \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3fbb87926..0a72a8159 100644 --- a/.gitignore +++ b/.gitignore @@ -115,4 +115,6 @@ firebase/.env logs/oAuthClient-log.log -.node-persist/** \ No newline at end of file +.node-persist/** + +/*.env.* \ No newline at end of file diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 999c45344..f311d7e00 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -40455,6 +40455,27 @@ + + cycle_time_analysis + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + estimates_written_converted false diff --git a/client/.env.development b/client/.env.development new file mode 100644 index 000000000..900d02994 --- /dev/null +++ b/client/.env.development @@ -0,0 +1,13 @@ +REACT_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql +REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql +REACT_APP_GA_CODE=231099835 +REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"} +REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test +REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test +REACT_APP_CLOUDINARY_API_KEY=957865933348715 +REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250 +REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4' +REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g +REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/ +REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online +REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc \ No newline at end of file diff --git a/client/.env.production b/client/.env.production new file mode 100644 index 000000000..7a58e4591 --- /dev/null +++ b/client/.env.production @@ -0,0 +1,13 @@ +REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql +REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql +REACT_APP_GA_CODE=231103507 +REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU","authDomain":"imex-prod.firebaseapp.com","databaseURL":"https://imex-prod.firebaseio.com","projectId":"imex-prod","storageBucket":"imex-prod.appspot.com","messagingSenderId":"253497221485","appId":"1:253497221485:web:3c81c483b94db84b227a64","measurementId":"G-NTWBKG2L0M"} +REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop +REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop +REACT_APP_CLOUDINARY_API_KEY=473322739956866 +REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250 +REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BMgZT1NZztW2DsJl8Mg2L04hgY9FzAg6b8fbzgNAfww2VDzH3VE63Ot9EaP_U7KWS2JT-7HPHaw0T_Tw_5vkZc8' +REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g +REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/ +REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online +REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk \ No newline at end of file diff --git a/client/.env.test b/client/.env.test new file mode 100644 index 000000000..c6403dee6 --- /dev/null +++ b/client/.env.test @@ -0,0 +1,14 @@ +REACT_APP_GRAPHQL_ENDPOINT=https://db.test.bodyshop.app/v1/graphql +REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.bodyshop.app/v1/graphql +REACT_APP_GA_CODE=231099835 +REACT_APP_FIREBASE_CONFIG={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"} +REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop +REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop +REACT_APP_CLOUDINARY_API_KEY=473322739956866 +REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250 +REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo' +REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g +REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/ +REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online +REACT_APP_IS_TEST=true +REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc \ No newline at end of file diff --git a/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx b/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx index 8f97a3008..872d2aec3 100644 --- a/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx +++ b/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx @@ -39,7 +39,13 @@ export function ContractsFindModalContainer({ if (!claim || !shortclaim) return; const trimmedShortClaim = shortclaim.trim(); // const trimmedClaim = claim.trim(); - claimNumbers.push({ claim: trimmedShortClaim, amount }); + if (amount.slice(-1) === "-") { + } + + claimNumbers.push({ + claim: trimmedShortClaim, + amount: amount.slice(-1) === "-" ? parseFloat(amount) * -1 : amount, + }); }); await GenerateDocument( diff --git a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx index 21281aa93..1da53e3ba 100644 --- a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx +++ b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx @@ -140,7 +140,10 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) { > - diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 7da393683..1ed3f526d 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -12,7 +12,7 @@ import Icon, { FileAddFilled, FileAddOutlined, FileFilled, - GlobalOutlined, + //GlobalOutlined, HomeFilled, ImportOutlined, LineChartOutlined, @@ -374,25 +374,27 @@ function Header({ {t("menus.currentuser.profile")} - - - {t("menus.currentuser.languageselector")} - - } - > - - {t("general.languages.english")} - - - {t("general.languages.french")} - - - {t("general.languages.spanish")} - - + { + // + // + // {t("menus.currentuser.languageselector")} + // + // } + // > + // + // {t("general.languages.english")} + // + // + // {t("general.languages.french")} + // + // + // {t("general.languages.spanish")} + // + // + } }> {recentItems.map((i, idx) => ( diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 4bf702cee..33363a5d3 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -855,7 +855,7 @@ export const QUERY_JOB_CARD_DETAILS = gql` count status } - joblines { + joblines(where: { removed: { _eq: false } }) { id mod_lbr_ty mod_lb_hrs diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 6cc02bf35..9c3ae03f0 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2099,7 +2099,7 @@ }, "labels": { "balance": "Balance", - "ca_bc_etf_table": "ICBC ETF Table Converter", + "ca_bc_etf_table": "ICBC EFT Table Converter", "customer": "Customer", "edit": "Edit Payment", "electronicpayment": "Use Electronic Payment Processing?", @@ -2275,7 +2275,7 @@ "title": "Print Center" }, "payments": { - "ca_bc_etf_table": "ICBC ETF Table", + "ca_bc_etf_table": "ICBC EFT Table", "exported_payroll": "Payroll Table" }, "special": { @@ -2403,6 +2403,7 @@ "credits_not_received_date": "Credits not Received by Date", "credits_not_received_date_vendorid": "Credits not Received by Vendor", "csi": "CSI Responses", + "cycle_time_analysis": "Cycle Time Analysis", "estimates_written_converted": "Estimates Written/Converted", "estimator_detail": "Jobs by Estimator (Detail)", "estimator_summary": "Jobs by Estimator (Summary)", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 992fd6c3b..6e7454cd0 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2403,6 +2403,7 @@ "credits_not_received_date": "", "credits_not_received_date_vendorid": "", "csi": "", + "cycle_time_analysis": "", "estimates_written_converted": "", "estimator_detail": "", "estimator_summary": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 9aea7d71f..683b09f2c 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2403,6 +2403,7 @@ "credits_not_received_date": "", "credits_not_received_date_vendorid": "", "csi": "", + "cycle_time_analysis": "", "estimates_written_converted": "", "estimator_detail": "", "estimator_summary": "", diff --git a/client/src/utils/DatePickerRanges.js b/client/src/utils/DatePickerRanges.js index d2c377a81..aeed206c5 100644 --- a/client/src/utils/DatePickerRanges.js +++ b/client/src/utils/DatePickerRanges.js @@ -22,5 +22,6 @@ const range = { moment().startOf("quarter"), moment().startOf("quarter").add(1, "quarter").subtract(1, "day"), ], + "Last 90 Days": [moment().add(-90, "days"), moment()], }; export default range; diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 78ee830cd..410cc0e2e 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -1682,6 +1682,18 @@ export const TemplateList = (type, context) => { }, group: "sales", }, + cycle_time_analysis: { + title: i18n.t("reportcenter.templates.cycle_time_analysis"), + subject: i18n.t("reportcenter.templates.cycle_time_analysis"), + key: "cycle_time_analysis", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.actual_completion"), + }, + group: "jobs", + }, } : {}), ...(!type || type === "courtesycarcontract" diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 00be05ed1..f1678c62e 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -3769,6 +3769,27 @@ - active: _eq: true event_triggers: + - name: job_status_transition + definition: + enable_manual: false + insert: + columns: '*' + update: + columns: + - status + retry_conf: + interval_sec: 10 + num_retries: 0 + timeout_sec: 60 + webhook_from_env: HASURA_API_URL + headers: + - name: event-secret + value_from_env: EVENT_SECRET + request_transform: + query_params: {} + template_engine: Kriti + url: '{{$base_url}}/job/statustransition' + version: 2 - name: jobs_arms definition: enable_manual: false diff --git a/server.js b/server.js index 327008c39..90d3da90f 100644 --- a/server.js +++ b/server.js @@ -129,7 +129,7 @@ var job = require("./server/job/job"); app.post("/job/totals", fb.validateFirebaseIdToken, job.totals); app.post( "/job/statustransition", - fb.validateFirebaseIdToken, + // fb.validateFirebaseIdToken, job.statustransition ); app.post("/job/totalsssu", fb.validateFirebaseIdToken, job.totalsSsu); diff --git a/server/job/job-status-transition.js b/server/job/job-status-transition.js index 0a41e7a6a..8ac622bef 100644 --- a/server/job/job-status-transition.js +++ b/server/job/job-status-transition.js @@ -7,22 +7,29 @@ const logger = require("../utils/logger"); // Dinero.defaultCurrency = "USD"; // Dinero.globalLocale = "en-CA"; Dinero.globalRoundingMode = "HALF_EVEN"; - +const path = require("path"); +const client = require("../graphql-client/graphql-client").client; +require("dotenv").config({ + path: path.resolve( + process.cwd(), + `.env.${process.env.NODE_ENV || "development"}` + ), +}); async function StatusTransition(req, res) { - const { jobid, value, bodyshopid } = req.body; - - const BearerToken = req.headers.authorization; - logger.log("job-costing-start", "DEBUG", req.user.email, jobid, null); - const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { - headers: { - Authorization: BearerToken, - }, - }); + if (req.headers["event-secret"] !== process.env.EVENT_SECRET) { + res.status(403).send("Unauthorized"); + return; + } + const { + id: jobid, + status: value, + shopid: bodyshopid, + } = req.body.event.data.new; try { - const { update_transitions } = await client - .setHeaders({ Authorization: BearerToken }) - .request(queries.UPDATE_OLD_TRANSITION, { + const { update_transitions } = await client.request( + queries.UPDATE_OLD_TRANSITION, + { jobid: jobid, existingTransition: { end: new Date(), @@ -30,7 +37,8 @@ async function StatusTransition(req, res) { //duration }, - }); + } + ); let duration = update_transitions.affected_rows === 0 @@ -38,29 +46,27 @@ async function StatusTransition(req, res) { : new Date(update_transitions.returning[0].end) - new Date(update_transitions.returning[0].start); - const resp2 = await client - .setHeaders({ Authorization: BearerToken }) - .request(queries.INSERT_NEW_TRANSITION, { - oldTransitionId: + const resp2 = await client.request(queries.INSERT_NEW_TRANSITION, { + oldTransitionId: + update_transitions.affected_rows === 0 + ? null + : update_transitions.returning[0].id, + duration, + newTransition: { + bodyshopid: bodyshopid, + jobid: jobid, + start: + update_transitions.affected_rows === 0 + ? new Date() + : update_transitions.returning[0].end, + prev_value: update_transitions.affected_rows === 0 ? null - : update_transitions.returning[0].id, - duration, - newTransition: { - bodyshopid: bodyshopid, - jobid: jobid, - start: - update_transitions.affected_rows === 0 - ? new Date() - : update_transitions.returning[0].end, - prev_value: - update_transitions.affected_rows === 0 - ? null - : update_transitions.returning[0].value, - value: value, - type: "status", - }, - }); + : update_transitions.returning[0].value, + value: value, + type: "status", + }, + }); //Check to see if there is an existing status transition record. //Query using Job ID, start is not null, end is null.