diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 068dc44aa..36431c54b 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -779,6 +779,13 @@ table: schema: public name: timetickets + - name: transitions + using: + foreign_key_constraint_on: + column: bodyshopid + table: + schema: public + name: transitions - name: vehicles using: foreign_key_constraint_on: @@ -2645,6 +2652,13 @@ table: schema: public name: timetickets + - name: transitions + using: + foreign_key_constraint_on: + column: jobid + table: + schema: public + name: transitions insert_permissions: - role: user permission: @@ -4597,6 +4611,93 @@ _eq: X-Hasura-User-Id - active: _eq: true +- table: + schema: public + name: transitions + object_relationships: + - name: bodyshop + using: + foreign_key_constraint_on: bodyshopid + - name: job + using: + foreign_key_constraint_on: jobid + insert_permissions: + - role: user + permission: + check: + bodyshop: + associations: + _and: + - active: + _eq: true + - user: + authid: + _eq: X-Hasura-User-Id + columns: + - bodyshopid + - created_at + - duration + - end + - id + - jobid + - next_value + - prev_value + - start + - type + - updated_at + - value + backend_only: false + select_permissions: + - role: user + permission: + columns: + - duration + - next_value + - prev_value + - type + - value + - created_at + - end + - start + - updated_at + - bodyshopid + - id + - jobid + filter: + bodyshop: + associations: + _and: + - active: + _eq: true + - user: + authid: + _eq: X-Hasura-User-Id + update_permissions: + - role: user + permission: + columns: + - duration + - next_value + - prev_value + - type + - value + - created_at + - end + - start + - updated_at + - bodyshopid + - id + - jobid + filter: + bodyshop: + associations: + _and: + - active: + _eq: true + - user: + authid: + _eq: X-Hasura-User-Id + check: {} - table: schema: public name: users diff --git a/hasura/migrations/1650994475711_create_table_public_transitions/down.sql b/hasura/migrations/1650994475711_create_table_public_transitions/down.sql new file mode 100644 index 000000000..d35eff59d --- /dev/null +++ b/hasura/migrations/1650994475711_create_table_public_transitions/down.sql @@ -0,0 +1 @@ +DROP TABLE "public"."transitions"; diff --git a/hasura/migrations/1650994475711_create_table_public_transitions/up.sql b/hasura/migrations/1650994475711_create_table_public_transitions/up.sql new file mode 100644 index 000000000..0c8d150d0 --- /dev/null +++ b/hasura/migrations/1650994475711_create_table_public_transitions/up.sql @@ -0,0 +1,18 @@ +CREATE TABLE "public"."transitions" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "bodyshoipid" uuid NOT NULL, "start" timestamptz NOT NULL, "end" timestamptz, "duration" numeric DEFAULT 0, "prev_value" text, "value" text, "next_value" Text, "jobid" uuid, "type" text NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("bodyshoipid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade); +CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"() +RETURNS TRIGGER AS $$ +DECLARE + _new record; +BEGIN + _new := NEW; + _new."updated_at" = NOW(); + RETURN _new; +END; +$$ LANGUAGE plpgsql; +CREATE TRIGGER "set_public_transitions_updated_at" +BEFORE UPDATE ON "public"."transitions" +FOR EACH ROW +EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"(); +COMMENT ON TRIGGER "set_public_transitions_updated_at" ON "public"."transitions" +IS 'trigger to set value of column "updated_at" to current timestamp on row update'; +CREATE EXTENSION IF NOT EXISTS pgcrypto; diff --git a/hasura/migrations/1650999118258_alter_table_public_transitions_alter_column_bodyshoipid/down.sql b/hasura/migrations/1650999118258_alter_table_public_transitions_alter_column_bodyshoipid/down.sql new file mode 100644 index 000000000..d95cb2a95 --- /dev/null +++ b/hasura/migrations/1650999118258_alter_table_public_transitions_alter_column_bodyshoipid/down.sql @@ -0,0 +1 @@ +alter table "public"."transitions" rename column "bodyshopid" to "bodyshoipid"; diff --git a/hasura/migrations/1650999118258_alter_table_public_transitions_alter_column_bodyshoipid/up.sql b/hasura/migrations/1650999118258_alter_table_public_transitions_alter_column_bodyshoipid/up.sql new file mode 100644 index 000000000..161ecbf25 --- /dev/null +++ b/hasura/migrations/1650999118258_alter_table_public_transitions_alter_column_bodyshoipid/up.sql @@ -0,0 +1 @@ +alter table "public"."transitions" rename column "bodyshoipid" to "bodyshopid"; diff --git a/server.js b/server.js index 4b9d076f0..d36a18e05 100644 --- a/server.js +++ b/server.js @@ -123,6 +123,11 @@ app.post("/sms/markConversationRead", smsStatus.markConversationRead); var job = require("./server/job/job"); app.post("/job/totals", fb.validateFirebaseIdToken, job.totals); +app.post( + "/job/statustransition", + fb.validateFirebaseIdToken, + job.statustransition +); app.post("/job/totalsssu", fb.validateFirebaseIdToken, job.totalsSsu); app.post("/job/costing", fb.validateFirebaseIdToken, job.costing); app.post("/job/costingmulti", fb.validateFirebaseIdToken, job.costingmulti); diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index fdd9d328f..1ec8de635 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -1489,3 +1489,37 @@ mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) { } } `; + +exports.QUERY_EXISTING_TRANSITION = ` +mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) { + insert_exportlog_one(object: $log) { + id + } +} +`; + +exports.UPDATE_OLD_TRANSITION = `mutation UPDATE_OLD_TRANSITION($jobid: uuid!, $existingTransition: transitions_set_input!){ + update_transitions(where:{jobid:{_eq:$jobid}, end:{_is_null:true + }}, _set:$existingTransition){ + affected_rows + returning{ + id + start + end + prev_value + next_value + value + } + } +}`; + +exports.INSERT_NEW_TRANSITION = `mutation INSERT_NEW_TRANSITION($newTransition: transitions_insert_input!, $oldTransitionId: uuid, $duration: numeric) { + insert_transitions_one(object: $newTransition) { + id + } + update_transitions(where: {id: {_eq: $oldTransitionId}}, _set: {duration: $duration}) { + affected_rows + } +} + +`; diff --git a/server/job/job-status-transition.js b/server/job/job-status-transition.js new file mode 100644 index 000000000..0a41e7a6a --- /dev/null +++ b/server/job/job-status-transition.js @@ -0,0 +1,84 @@ +const Dinero = require("dinero.js"); +const queries = require("../graphql-client/queries"); +//const client = require("../graphql-client/graphql-client").client; +const _ = require("lodash"); +const GraphQLClient = require("graphql-request").GraphQLClient; +const logger = require("../utils/logger"); +// Dinero.defaultCurrency = "USD"; +// Dinero.globalLocale = "en-CA"; +Dinero.globalRoundingMode = "HALF_EVEN"; + +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, + }, + }); + + try { + const { update_transitions } = await client + .setHeaders({ Authorization: BearerToken }) + .request(queries.UPDATE_OLD_TRANSITION, { + jobid: jobid, + existingTransition: { + end: new Date(), + next_value: value, + + //duration + }, + }); + + let duration = + update_transitions.affected_rows === 0 + ? 0 + : 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: + 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", + }, + }); + + //Check to see if there is an existing status transition record. + //Query using Job ID, start is not null, end is null. + + //If there is no existing record, this is the start of the transition life cycle. + // Create the initial transition record. + + //If there is a current status transition record, update it with the end date, duration, and next value. + + res.sendStatus(200); //.json(ret); + } catch (error) { + logger.log("job-costing-error", "ERROR", req.user.email, jobid, { + message: error.message, + stack: error.stack, + }); + + res.status(400).send(JSON.stringify(error)); + } +} + +exports.statustransition = StatusTransition; diff --git a/server/job/job.js b/server/job/job.js index 975342045..e0fff1695 100644 --- a/server/job/job.js +++ b/server/job/job.js @@ -2,3 +2,4 @@ exports.totals = require("./job-totals").default; exports.totalsSsu = require("./job-totals").totalsSsu; exports.costing = require("./job-costing").JobCosting; exports.costingmulti = require("./job-costing").JobCostingMulti; +exports.statustransition = require("./job-status-transition").statustransition;