Compare commits

...

8 Commits

Author SHA1 Message Date
Allan Carr
92fd5b0315 IO-3503 Job Costing Bug Fix
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-01-14 18:02:26 -08:00
Dave Richer
be2df79555 Merged in hotfix/2026-01-13 (pull request #2813)
Hotfix/2026 01 13 into master-AIO
2026-01-13 20:52:00 +00:00
Allan Carr
9c733702e4 Merged in feature/IO-3498-QBO-Auth-Token (pull request #2810)
IO-3498 QBO Auth Token

Approved-by: Dave Richer
2026-01-13 20:45:42 +00:00
Allan Carr
997aebddb0 IO-3498 QBO Auth Token
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-01-13 12:42:18 -08:00
Dave
17e3f39706 Merge remote-tracking branch 'origin/feature/IO-3431-Job-Image-Gallery' into hotfix/2026-01-13 2026-01-13 14:22:23 -05:00
Allan Carr
d90acf4b89 IO-3431 Prettier Run
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-01-12 13:27:23 -08:00
Allan Carr
68dd7f33ab IO-3431 Add Tags to Images and Documents
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-01-12 13:25:26 -08:00
Dave Richer
2a931181d1 Merged in hotfix/2026-01-12 (pull request #2804)
Hotfix/2026 01 12
2026-01-12 17:19:48 +00:00
4 changed files with 51 additions and 15 deletions

View File

@@ -67,6 +67,7 @@ export function JobsDocumentsLocalGallery({
src: val.thumbnail,
height: val.thumbnailHeight,
width: val.thumbnailWidth,
tags: [{ value: val.filename, title: val.filename }],
...(val.optimized && { src: val.optimized, fullsize: val.src })
});
if (val.optimized) optimized = true;

View File

@@ -1,4 +1,5 @@
import { Checkbox } from "antd";
import { Tag } from "antd";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
/**
@@ -187,6 +188,30 @@ export function LocalMediaGrid({
transition: "opacity .25s ease"
}}
/>
{img.tags && img.tags.length > 0 && (
<div
style={{
position: "absolute",
bottom: 0,
left: 0,
right: 0,
padding: "2px 6px",
borderRadius: "0 0 4px 4px",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
display: "flex",
flexWrap: "wrap",
gap: "4px"
}}
>
{img.tags.map((tag, tagIdx) => (
<Tag key={tagIdx} variant="outlined" color="gold" style={{ opacity: 0.8 }}>
{tag.value || tag.title}
</Tag>
))}
</div>
)}
</>
);
})()}

View File

@@ -45,7 +45,7 @@ exports.default = async (req, res) => {
exports.refresh = async (oauthClient, req) => {
try {
// logger.log("qbo-token-refresh", "DEBUG", req.user.email, null, null);
logger.log("qbo-token-refresh", "DEBUG", req.user.email, null, null);
const authResponse = await oauthClient.refresh();
await client.request(queries.SET_QBO_AUTH, {
email: req.user.email,
@@ -59,10 +59,12 @@ exports.refresh = async (oauthClient, req) => {
};
exports.setNewRefreshToken = async (email, apiResponse) => {
//logger.log("qbo-token-updated", "DEBUG", email, null, null);
// Deprecated - tokens are now auto-updated in the oauthClient and the token isn't pushed back from QBO API calls anymore
await client.request(queries.SET_QBO_AUTH, {
email,
qbo_auth: { ...apiResponse.token, createdAt: Date.now() }
});
// logger.log("qbo-token-updated", "DEBUG", email, null, {apiResponse: apiResponse});
// await client.request(queries.SET_QBO_AUTH, {
// email,
// qbo_auth: { ...apiResponse.token, createdAt: Date.now() }
// });
};

View File

@@ -304,6 +304,7 @@ function GenerateCostingData(job) {
if (
job.cieca_pfl &&
job.cieca_pfl[val.mod_lbr_ty.toUpperCase()] &&
typeof job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp === "number" &&
job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp !== 0
) {
let adjp = 0;
@@ -338,7 +339,7 @@ function GenerateCostingData(job) {
if (!acc.labor[laborProfitCenter]) acc.labor[laborProfitCenter] = Dinero();
acc.labor[laborProfitCenter] = acc.labor[laborProfitCenter].add(laborAmount);
if (val.mod_lb_hrs === 0 && val.act_price > 0 && val.lbr_op === "OP14") {
if (val.act_price > 0 && val.lbr_op === "OP14") {
//Scenario where SGI may pay out hours using a part price.
acc.labor[laborProfitCenter] = acc.labor[laborProfitCenter].add(
Dinero({
@@ -469,10 +470,7 @@ function GenerateCostingData(job) {
}
//Additional Profit Center
if (
(!val.part_type && !val.mod_lbr_ty) ||
(!val.part_type && val.mod_lbr_ty && val.act_price > 0 && val.lbr_op !== "OP14")
) {
if ((!val.part_type && !val.mod_lbr_ty) || (!val.part_type && val.mod_lbr_ty && val.lbr_op !== "OP14")) {
//Does it already have a defined profit center?
//If so, use it, otherwise try to use the same from the auto-allocate logic in IO app jobs-close-auto-allocate.
const partsProfitCenter = val.profitcenter_part || getAdditionalCostCenter(val, defaultProfits) || "Unknown";
@@ -524,7 +522,12 @@ function GenerateCostingData(job) {
}).multiply(materialsHours.mapaHrs || 0)
);
let adjp = 0;
if (job.materials["MAPA"] && job.materials["MAPA"].mat_adjp) {
if (
job.materials["MAPA"] &&
job.materials["MAPA"].mat_adjp &&
typeof job.materials["MAPA"].mat_adjp === "number" &&
job.materials["MAPA"].mat_adjp !== 0
) {
adjp =
Math.abs(job.materials["MAPA"].mat_adjp) > 1
? job.materials["MAPA"].mat_adjp
@@ -551,7 +554,12 @@ function GenerateCostingData(job) {
}).multiply(materialsHours.mashHrs || 0)
);
let adjp = 0;
if (job.materials["MASH"] && job.materials["MASH"].mat_adjp) {
if (
job.materials["MASH"] &&
job.materials["MASH"].mat_adjp &&
typeof job.materials["MASH"].mat_adjp === "number" &&
job.materials["MASH"].mat_adjp !== 0
) {
adjp =
Math.abs(job.materials["MASH"].mat_adjp) > 1
? job.materials["MASH"].mat_adjp
@@ -575,7 +583,7 @@ function GenerateCostingData(job) {
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = Dinero();
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = stlTowing
? Dinero({ amount: Math.round(stlTowing.ttl_amt * 100) })
? Dinero({ amount: Math.round((stlTowing.ttl_amt || 0) * 100) })
: Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
});
@@ -584,7 +592,7 @@ function GenerateCostingData(job) {
jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]] = Dinero();
jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]] = stlStorage
? Dinero({ amount: Math.round(stlStorage.ttl_amt * 100) })
? Dinero({ amount: Math.round((stlStorage.ttl_amt || 0) * 100) })
: Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
});