Compare commits
17 Commits
release/20
...
bugfix/IO-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1566084d9c | ||
|
|
849d967b56 | ||
|
|
519d7e8d87 | ||
|
|
b08435607e | ||
|
|
ea9e4ffcad | ||
|
|
6c814c7dc6 | ||
|
|
cc9e536059 | ||
|
|
dadc9892d0 | ||
|
|
b05e20ce0d | ||
|
|
eb36b12cb0 | ||
|
|
bf5a099fa6 | ||
|
|
ff3d24c623 | ||
|
|
27b955a701 | ||
|
|
3d9ad799f3 | ||
|
|
6e17ef10bb | ||
|
|
f76165552e | ||
|
|
80fbb847d8 |
@@ -48,7 +48,7 @@ export function BillDetailEditReturn({ setPartsOrderContext, data, disabled }) {
|
||||
// db_price: i.actual_price,
|
||||
act_price: i.actual_price,
|
||||
cost: i.actual_cost,
|
||||
quantity: i.quantity,
|
||||
part_qty: i.quantity,
|
||||
joblineid: i.joblineid,
|
||||
oem_partno: i.jobline && i.jobline.oem_partno,
|
||||
part_type: i.jobline && i.jobline.part_type
|
||||
@@ -104,6 +104,10 @@ export function BillDetailEditReturn({ setPartsOrderContext, data, disabled }) {
|
||||
{fields.map((field, index) => (
|
||||
<tr key={field.key}>
|
||||
<td>
|
||||
{/* Hidden field to preserve the id */}
|
||||
<Form.Item name={[field.name, "id"]} hidden>
|
||||
<input type="hidden" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
// label={t("joblines.fields.selected")}
|
||||
key={`${index}selected`}
|
||||
|
||||
@@ -49,12 +49,15 @@ export function DmsCdkVehicles({ form, job }) {
|
||||
open={open}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={() => {
|
||||
form.setFieldsValue({
|
||||
dms_make: selectedModel.makecode,
|
||||
dms_model: selectedModel.modelcode
|
||||
});
|
||||
setOpen(false);
|
||||
if (selectedModel) {
|
||||
form.setFieldsValue({
|
||||
dms_make: selectedModel.makecode,
|
||||
dms_model: selectedModel.modelcode
|
||||
});
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
okButtonProps={{ disabled: !selectedModel }}
|
||||
>
|
||||
{error && <AlertComponent title={error.message} type="error" />}
|
||||
<Table
|
||||
|
||||
@@ -42,6 +42,10 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
|
||||
<tbody>
|
||||
{fields.map((field, index) => (
|
||||
<tr key={field.key}>
|
||||
{/* Hidden field to preserve jobline ID */}
|
||||
<Form.Item hidden name={[field.name, "id"]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<td>
|
||||
<Form.Item
|
||||
// label={t("joblines.fields.line_desc")}
|
||||
|
||||
@@ -81,16 +81,17 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
const employeeData = bodyshop.associations.find((a) => a.useremail === job.admin_clerk)?.user?.employee ?? null;
|
||||
|
||||
// Handle checkbox changes
|
||||
const handleCheckboxChange = async (field, checked) => {
|
||||
const handleCheckboxChange = async (field, e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const checked = e.target.checked;
|
||||
const value = checked ? dayjs().toISOString() : null;
|
||||
try {
|
||||
const ret = await updateJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: { [field]: value }
|
||||
},
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
awaitRefetchQueries: true
|
||||
}
|
||||
});
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
@@ -182,7 +183,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
<Space>
|
||||
<Checkbox
|
||||
checked={!!job.estimate_sent_approval}
|
||||
onChange={(e) => handleCheckboxChange("estimate_sent_approval", e.target.checked)}
|
||||
onChange={(e) => handleCheckboxChange("estimate_sent_approval", e)}
|
||||
disabled={disabled || isPartsEntry}
|
||||
>
|
||||
{job.estimate_sent_approval && (
|
||||
@@ -197,7 +198,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
|
||||
<Space>
|
||||
<Checkbox
|
||||
checked={!!job.estimate_approved}
|
||||
onChange={(e) => handleCheckboxChange("estimate_approved", e.target.checked)}
|
||||
onChange={(e) => handleCheckboxChange("estimate_approved", e)}
|
||||
disabled={disabled || isPartsEntry}
|
||||
>
|
||||
{job.estimate_approved && (
|
||||
|
||||
@@ -13,6 +13,13 @@ import PartsOrderModalPriceChange from "./parts-order-modal-price-change.compone
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import { selectIsPartsEntry } from "../../redux/application/application.selectors.js";
|
||||
|
||||
const PriceInputWrapper = ({ value, onChange, form, field }) => (
|
||||
<Space.Compact style={{ width: "100%" }}>
|
||||
<PartsOrderModalPriceChange form={form} field={field} />
|
||||
<CurrencyInput style={{ flex: 1 }} value={value} onChange={onChange} />
|
||||
</Space.Compact>
|
||||
);
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
isPartsEntry: selectIsPartsEntry
|
||||
@@ -199,10 +206,7 @@ export function PartsOrderModalComponent({
|
||||
key={`${index}act_price`}
|
||||
name={[field.name, "act_price"]}
|
||||
>
|
||||
<Space.Compact style={{ width: "100%" }}>
|
||||
<PartsOrderModalPriceChange form={form} field={field} />
|
||||
<CurrencyInput style={{ flex: 1 }} />
|
||||
</Space.Compact>
|
||||
<PriceInputWrapper form={form} field={field} />
|
||||
</Form.Item>
|
||||
{isReturn && (
|
||||
<Form.Item
|
||||
|
||||
@@ -89,7 +89,8 @@ export function PartsOrderModalContainer({
|
||||
|
||||
return {
|
||||
...p,
|
||||
job_line_id: jobLineId
|
||||
job_line_id: jobLineId,
|
||||
...(isReturn && { cm_received: false })
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -33,8 +33,7 @@ export function TaskListContainer({
|
||||
currentUser,
|
||||
onlyMine,
|
||||
parentJobId,
|
||||
showRo = true,
|
||||
disableJobRefetch = false
|
||||
showRo = true
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const notification = useNotification();
|
||||
@@ -91,10 +90,6 @@ export function TaskListContainer({
|
||||
refetchQueries: [Object.keys(query)[0]]
|
||||
};
|
||||
|
||||
if (!disableJobRefetch) {
|
||||
toggledTaskObject.refetchQueries.push("GET_JOB_BY_PK");
|
||||
}
|
||||
|
||||
const toggledTask = await toggleTaskCompleted(toggledTaskObject);
|
||||
|
||||
if (!toggledTask.errors) {
|
||||
@@ -144,10 +139,6 @@ export function TaskListContainer({
|
||||
refetchQueries: [Object.keys(query)[0]]
|
||||
};
|
||||
|
||||
if (!disableJobRefetch) {
|
||||
toggledTaskObject.refetchQueries.push("GET_JOB_BY_PK");
|
||||
}
|
||||
|
||||
const toggledTask = await toggleTaskDeleted(toggledTaskObject);
|
||||
|
||||
if (!toggledTask.errors) {
|
||||
|
||||
@@ -356,7 +356,10 @@ export const MUTATION_BACKORDER_PART_LINE = gql`
|
||||
export const QUERY_UNRECEIVED_LINES = gql`
|
||||
query QUERY_UNRECEIVED_LINES($jobId: uuid!, $vendorId: uuid!) {
|
||||
parts_order_lines(
|
||||
where: { parts_order: { jobid: { _eq: $jobId }, vendorid: { _eq: $vendorId } }, cm_received: { _neq: true } }
|
||||
where: {
|
||||
parts_order: { jobid: { _eq: $jobId }, vendorid: { _eq: $vendorId }, return: { _eq: true } }
|
||||
_or: [{ cm_received: { _neq: true } }, { cm_received: { _is_null: true } }]
|
||||
}
|
||||
) {
|
||||
cm_received
|
||||
id
|
||||
|
||||
@@ -82,10 +82,23 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
|
||||
|
||||
const handleFinish = async ({ removefromproduction, ...values }) => {
|
||||
setLoading(true);
|
||||
|
||||
// Validate that all joblines have valid IDs
|
||||
const joblinesWithIds = values.joblines.filter(jl => jl && jl.id);
|
||||
if (joblinesWithIds.length !== values.joblines.length) {
|
||||
notification.error({
|
||||
title: t("jobs.errors.invalidjoblines"),
|
||||
message: t("jobs.errors.missingjoblineids")
|
||||
});
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await client.mutate({
|
||||
mutation: generateJobLinesUpdatesForInvoicing(values.joblines)
|
||||
});
|
||||
if (result.errors) {
|
||||
setLoading(false);
|
||||
return; // Abandon the rest of the close.
|
||||
}
|
||||
|
||||
|
||||
@@ -23,17 +23,10 @@ export function TasksPageComponent({ bodyshop, currentUser, type }) {
|
||||
relationshipType={"assigned_to"}
|
||||
query={{ QUERY_MY_TASKS_PAGINATED }}
|
||||
titleTranslation={"tasks.titles.my_tasks"}
|
||||
disableJobRefetch={true}
|
||||
/>
|
||||
);
|
||||
case taskPageTypes.ALL_TASKS:
|
||||
return (
|
||||
<TaskListContainer
|
||||
query={{ QUERY_ALL_TASKS_PAGINATED }}
|
||||
titleTranslation={"tasks.titles.all_tasks"}
|
||||
disableJobRefetch={true}
|
||||
/>
|
||||
);
|
||||
return <TaskListContainer query={{ QUERY_ALL_TASKS_PAGINATED }} titleTranslation={"tasks.titles.all_tasks"} />;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -1676,7 +1676,9 @@
|
||||
"deleted": "Error deleting Job. {{error}}",
|
||||
"exporting": "Error exporting Job. {{error}}",
|
||||
"exporting-partner": "Unable to connect to partner application. Please ensure it is running and logged in.",
|
||||
"invalidjoblines": "Job has invalid job lines.",
|
||||
"invoicing": "Error invoicing Job. {{error}}",
|
||||
"missingjoblineids": "Missing job line IDs for job lines.",
|
||||
"noaccess": "This Job does not exist or you do not have access to it.",
|
||||
"nodamage": "No damage points on estimate.",
|
||||
"nodates": "No dates specified for this Job.",
|
||||
|
||||
@@ -1674,7 +1674,9 @@
|
||||
"deleted": "Error al eliminar el trabajo.",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidjoblines": "",
|
||||
"invoicing": "",
|
||||
"missingjoblineids": "",
|
||||
"noaccess": "Este trabajo no existe o no tiene acceso a él.",
|
||||
"nodamage": "",
|
||||
"nodates": "No hay fechas especificadas para este trabajo.",
|
||||
|
||||
@@ -1674,7 +1674,9 @@
|
||||
"deleted": "Erreur lors de la suppression du travail.",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidjoblines": "",
|
||||
"invoicing": "",
|
||||
"missingjoblineids": "",
|
||||
"noaccess": "Ce travail n'existe pas ou vous n'y avez pas accès.",
|
||||
"nodamage": "",
|
||||
"nodates": "Aucune date spécifiée pour ce travail.",
|
||||
|
||||
Reference in New Issue
Block a user