|
{j.ro_number}
+ {j.status}
|
@@ -142,7 +143,7 @@ export function ScheduleCalendarHeaderComponent({
title={t("appointments.labels.arrivingjobs")}
>
- {(loadData.hoursIn || 0) && loadData.hoursIn.toFixed(2)}
+ {(loadData.allHoursIn || 0) && loadData.allHoursIn.toFixed(2)}
- {(loadData.hoursOut || 0) && loadData.hoursOut.toFixed(2)}
+ {(loadData.allHoursOut || 0) && loadData.allHoursOut.toFixed(2)}
diff --git a/client/src/graphql/appointments.queries.js b/client/src/graphql/appointments.queries.js
index 6a95864d8..01399b81a 100644
--- a/client/src/graphql/appointments.queries.js
+++ b/client/src/graphql/appointments.queries.js
@@ -294,6 +294,12 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
where: { inproduction: { _eq: true }, suspended: { _eq: false } }
) {
id
+ actual_in
+ scheduled_in
+ actual_completion
+ scheduled_completion
+ inproduction
+ ro_number
labhrs: joblines_aggregate(
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
) {
@@ -327,12 +333,15 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
}
) {
id
+ status
ro_number
scheduled_completion
actual_completion
+ scheduled_in
ownr_fn
ownr_ln
ownr_co_nm
+ inproduction
labhrs: joblines_aggregate(
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
) {
@@ -360,11 +369,16 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
) {
id
scheduled_in
+ actual_in
+ scheduled_completion
ro_number
ownr_fn
ownr_ln
ownr_co_nm
alt_transport
+ actual_completion
+ inproduction
+ status
labhrs: joblines_aggregate(
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
) {
diff --git a/client/src/redux/application/application.sagas.js b/client/src/redux/application/application.sagas.js
index a768b3b93..e2dc607df 100644
--- a/client/src/redux/application/application.sagas.js
+++ b/client/src/redux/application/application.sagas.js
@@ -37,7 +37,7 @@ export function* calculateScheduleLoad({ payload: end }) {
productionTotal: {},
productionHours: 0,
};
-
+ let problemJobs = [];
//Set the current load.
buckets.forEach((bucket) => {
load.productionTotal[bucket.id] = { count: 0, label: bucket.label };
@@ -45,6 +45,32 @@ export function* calculateScheduleLoad({ payload: end }) {
prodJobs.forEach((item) => {
//Add all of the jobs currently in production to the buckets so that we have a starting point.
+ if (
+ !item.actual_completion &&
+ moment(item.scheduled_completion).isBefore(moment().startOf("day"))
+ ) {
+ problemJobs.push({
+ ...item,
+ code: "Job was scheduled to go, but it has not been completed. Update the scheduled completion date to correct projections",
+ });
+ }
+
+ if (
+ item.actual_completion &&
+ moment(item.actual_completion).isBefore(moment().startOf("day"))
+ ) {
+ problemJobs.push({
+ ...item,
+ code: "Job is already marked as completed, but it is still in production. This job should be removed from production",
+ });
+ }
+ if (!(item.actual_completion || item.scheduled_completion)) {
+ problemJobs.push({
+ ...item,
+ code: "Job does not have a scheduled or actual completion date. Update the scheduled or actual completion dates to correct projections",
+ });
+ }
+
const bucketId = CheckJobBucket(buckets, item);
load.productionHours =
load.productionHours +
@@ -59,77 +85,119 @@ export function* calculateScheduleLoad({ payload: end }) {
});
arrJobs.forEach((item) => {
- if (!item.scheduled_in)
+ if (!item.scheduled_in) {
console.log("JOB HAS NO SCHEDULED IN DATE.", item);
- const itemDate = moment(item.scheduled_in).format("yyyy-MM-DD");
+ problemJobs.push({
+ ...item,
+ code: "Job has no scheduled in date",
+ });
+ }
+ if (
+ item.actual_completion &&
+ moment(item.actual_completion).isAfter(moment())
+ ) {
+ problemJobs.push({
+ ...item,
+ code: "Job has an actual completion date set in the future",
+ });
+ }
+ if (item.actual_completion && item.inproduction) {
+ problemJobs.push({
+ ...item,
+ code: "Job has an actual completion date but it is still marked in production",
+ });
+ }
+
+ const itemDate = moment(item.actual_in || item.scheduled_in).format(
+ "yyyy-MM-DD"
+ );
+
+ const AddJobForSchedulingCalc =
+ !item.inproduction &&
+ !moment(item.actual_completion || item.scheduled_completion).isSame(
+ moment(item.actual_in || item.scheduled_in),
+ "day"
+ );
+
if (!!load[itemDate]) {
- load[itemDate].hoursIn =
- (load[itemDate].hoursIn || 0) +
+ load[itemDate].allHoursIn =
+ (load[itemDate].allHoursIn || 0) +
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs;
- load[itemDate].jobsIn.push(item);
+
+ //If the job hasn't already arrived, add it to the jobs in list.
+ // Make sure it also hasn't already been completed, or isn't an in and out job.
+ //This prevents the duplicate counting.
+ load[itemDate].allJobsIn.push(item);
+ if (AddJobForSchedulingCalc) {
+ load[itemDate].jobsIn.push(item);
+ load[itemDate].hoursIn =
+ (load[itemDate].hoursIn || 0) +
+ item.labhrs.aggregate.sum.mod_lb_hrs +
+ item.larhrs.aggregate.sum.mod_lb_hrs;
+ }
} else {
load[itemDate] = {
- jobsIn: [item],
+ allJobsIn: [item],
+ jobsIn: AddJobForSchedulingCalc ? [item] : [], //Same as above, only add it if it isn't already in production.
jobsOut: [],
- hoursIn:
+ allJobsOut: [],
+ allHoursIn:
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs,
+ hoursIn: AddJobForSchedulingCalc
+ ? item.labhrs.aggregate.sum.mod_lb_hrs +
+ item.larhrs.aggregate.sum.mod_lb_hrs
+ : 0,
};
}
});
- let problemJobs = [];
compJobs.forEach((item) => {
if (!(item.actual_completion || item.scheduled_completion))
- console.log("JOB HAS NO COMPLETION DATE.", item);
+ console.warn("JOB HAS NO COMPLETION DATE.", item);
const inProdJobs = prodJobs.find((p) => p.id === item.id);
const inArrJobs = arrJobs.find((p) => p.id === item.id);
- if (!(inProdJobs || inArrJobs)) {
- //Job isn't found in production or coming in.
- //is it going today or scheduled to go today?
- if (
- moment(item.actual_completion || item.scheduled_completion).isSame(
- moment(),
- "day"
- )
- ) {
- console.log("Job is going today anyways, ignore it.", item);
- return;
- }
-
- if (
- moment(item.actual_completion || item.scheduled_completion).isBefore(
- moment(),
- "day"
- )
- ) {
- console.log("Job should have already gone. Ignoring it.", item);
- return;
- }
-
- problemJobs.push({
- ...item,
- code: "Job is scheduled for completion, but it is not marked in production nor is it an arriving job in this period. Check the scheduled in and completion dates",
- });
- return;
- }
+ const AddJobForSchedulingCalc =
+ item.inproduction &&
+ !moment(item.actual_completion || item.scheduled_completion).isSame(
+ moment(item.scheduled_in),
+ "day"
+ ) &&
+ (inProdJobs || inArrJobs);
const itemDate = moment(
item.actual_completion || item.scheduled_completion
).format("yyyy-MM-DD");
+ //Skip it, it's already completed.
+
if (!!load[itemDate]) {
- load[itemDate].hoursOut =
- (load[itemDate].hoursOut || 0) +
+ load[itemDate].allHoursOut =
+ (load[itemDate].allHoursOut || 0) +
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs;
- load[itemDate].jobsOut.push(item);
+ //Add only the jobs that are still in production to get rid of.
+ //If it's not in production, we'd subtract unnecessarily.
+ load[itemDate].allJobsOut.push(item);
+
+ if (AddJobForSchedulingCalc) {
+ load[itemDate].jobsOut.push(item);
+ load[itemDate].hoursOut =
+ (load[itemDate].hoursOut || 0) +
+ item.labhrs.aggregate.sum.mod_lb_hrs +
+ item.larhrs.aggregate.sum.mod_lb_hrs;
+ }
} else {
load[itemDate] = {
- jobsOut: [item],
- hoursOut:
+ allJobsOut: [item],
+ jobsOut: AddJobForSchedulingCalc ? [item] : [], //Same as above.
+ hoursOut: AddJobForSchedulingCalc
+ ? item.labhrs.aggregate.sum.mod_lb_hrs +
+ item.larhrs.aggregate.sum.mod_lb_hrs
+ : 0,
+ allHoursOut:
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs,
};
@@ -137,6 +205,7 @@ export function* calculateScheduleLoad({ payload: end }) {
});
//Propagate the expected load to each day.
+
const range = Math.round(moment.duration(end.diff(today)).asDays());
for (var day = 0; day < range; day++) {
const current = moment(today).add(day, "days").format("yyyy-MM-DD");
@@ -146,6 +215,7 @@ export function* calculateScheduleLoad({ payload: end }) {
if (!!!load[current]) {
load[current] = {};
}
+
if (day === 0) {
//Starting on day 1. The load is current.
load[current].expectedLoad = CalculateLoad(
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index d8e7bc391..23f24f79d 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -49,7 +49,7 @@
"blocked": "Blocked",
"cancelledappointment": "Canceled appointment for: ",
"completingjobs": "Completing Jobs",
- "dataconsistency": "{{ro_number}} has a data consistency issue. It has been excluded for scheduling purposes. CODE: {{code}}.",
+ "dataconsistency": "{{ro_number}} has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.",
"expectedjobs": "Expected Jobs in Production: ",
"expectedprodhrs": "Expected Production Hours:",
"history": "History",
@@ -2425,6 +2425,8 @@
"export_payables": "Export Log - Payables",
"export_payments": "Export Log - Payments",
"export_receivables": "Export Log - Receivables",
+ "gsr_by_atp": "Gross Sales by ATP",
+ "gsr_by_category": "Gross Sales by Category",
"gsr_by_csr": "Gross Sales by CSR",
"gsr_by_delivery_date": "Gross Sales by Delivery Date",
"gsr_by_estimator": "Gross Sales by Estimator",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 359d844a2..10385ab2e 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -2425,6 +2425,8 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
+ "gsr_by_atp": "",
+ "gsr_by_category": "",
"gsr_by_csr": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 78f2d8777..ee9ffb395 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -2425,6 +2425,8 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
+ "gsr_by_atp": "",
+ "gsr_by_category": "",
"gsr_by_csr": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "",
diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js
index a53040f0f..8869a5b2c 100644
--- a/client/src/utils/TemplateConstants.js
+++ b/client/src/utils/TemplateConstants.js
@@ -1343,6 +1343,32 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
+ gsr_by_category: {
+ title: i18n.t("reportcenter.templates.gsr_by_category"),
+ description: "",
+ subject: i18n.t("reportcenter.templates.gsr_by_category"),
+ key: "gsr_by_category",
+ //idtype: "vendor",
+ disabled: false,
+ rangeFilter: {
+ object: i18n.t("reportcenter.labels.objects.jobs"),
+ field: i18n.t("jobs.fields.date_invoiced"),
+ },
+ group: "sales",
+ },
+ gsr_by_atp: {
+ title: i18n.t("reportcenter.templates.gsr_by_atp"),
+ description: "",
+ subject: i18n.t("reportcenter.templates.gsr_by_atp"),
+ key: "gsr_by_atp",
+ //idtype: "vendor",
+ disabled: false,
+ rangeFilter: {
+ object: i18n.t("reportcenter.labels.objects.jobs"),
+ field: i18n.t("jobs.fields.date_invoiced"),
+ },
+ group: "sales",
+ },
gsr_labor_only: {
title: i18n.t("reportcenter.templates.gsr_labor_only"),
description: "",
diff --git a/server/email/sendemail.js b/server/email/sendemail.js
index 68c57a38d..28c0adf5d 100644
--- a/server/email/sendemail.js
+++ b/server/email/sendemail.js
@@ -23,6 +23,7 @@ let transporter = nodemailer.createTransport({
});
exports.sendServerEmail = async function ({ subject, text }) {
+ if (process.env.NODE_ENV === undefined) return;
try {
transporter.sendMail(
{
|