- Reload the page. Do not quote the customer a figure until it loads.
-
+
Reload the page.
@@ -98,8 +96,11 @@
data-automation-id="JobFinishTab-over-invoiced"
class="mt-3 rounded-md bg-amber-50 border border-amber-200 p-3 text-sm text-amber-900"
>
- Over-invoiced by {{ formatCurrency(summary.over_invoiced_excl_gst) }}
- (excl GST). Resolve it with a credit note in Xero.
+ Job appears over-invoiced by
+ {{ formatCurrency(summary.over_invoiced_excl_gst) }}
+ (excl GST). The job value may be out of date, or invoicing may need correcting.
@@ -118,7 +119,7 @@
class="bg-white rounded-xl border border-slate-200 p-4"
>
Completion checklist
-
Recorded against the job. Never blocks invoicing.
+
Self-checklist.
-
-
- On a Time & Materials job the time and materials are what the customer pays. Get them
- right before invoicing.
-
@@ -375,14 +367,21 @@ const checklist = reactive({
})
const savingChecklistKey = ref(null)
-const checklistItems: Array<{ key: ChecklistItemKey; label: string }> = [
- { key: 'foreman_signed_off', label: 'Has the foreman signed off the job?' },
- { key: 'timesheets_collected', label: 'Have you collected the timesheet entries?' },
- { key: 'materials_checked', label: 'Have you checked the materials on the job?' },
+const allChecklistItems: Array<{ key: ChecklistItemKey; label: string }> = [
+ { key: 'foreman_signed_off', label: 'Has the supervisor signed off the job?' },
+ { key: 'timesheets_collected', label: "Have you collected today's timesheet entries?" },
+ { key: 'materials_checked', label: 'Are all materials used written correctly on the job sheet?' },
{ key: 'customer_called', label: 'Have you called the customer?' },
- { key: 'released', label: 'Has the job been released?' },
+ { key: 'released', label: 'Has the job been handed over?' },
]
+// Timesheets only exist to invoice on a T&M job; a quoted job is never asked.
+const checklistItems = computed(() =>
+ props.pricingMethodology === 'time_materials'
+ ? allChecklistItems
+ : allChecklistItems.filter((item) => item.key !== 'timesheets_collected'),
+)
+
const jobValueLabel = computed(() =>
props.pricingMethodology === 'fixed_price'
? 'Quote total (excl GST)'
diff --git a/frontend/src/components/job/__tests__/JobFinishTab.checklist.test.ts b/frontend/src/components/job/__tests__/JobFinishTab.checklist.test.ts
index c91bb1450..499392bac 100644
--- a/frontend/src/components/job/__tests__/JobFinishTab.checklist.test.ts
+++ b/frontend/src/components/job/__tests__/JobFinishTab.checklist.test.ts
@@ -60,33 +60,23 @@ describe('JobFinishTab completion checklist', () => {
afterEach(resetFinishTab)
- it('asks the same five questions whatever the pricing methodology', async () => {
- for (const methodology of ['fixed_price', 'time_materials']) {
- const wrapper = mountTab(methodology)
- await flushPromises()
-
- for (const key of ITEMS) {
- expect(item(wrapper, key).exists(), `${methodology}/${key}`).toBe(true)
- }
-
- resetFinishTab()
- }
- })
-
- it('warns that time and materials are what a T&M customer pays', async () => {
+ it('asks all five questions on a T&M job', async () => {
const wrapper = mountTab('time_materials')
await flushPromises()
- expect(wrapper.find('[data-automation-id="JobFinishTab-tm-urgency"]').text()).toContain(
- 'before invoicing',
- )
+ for (const key of ITEMS) {
+ expect(item(wrapper, key).exists(), key).toBe(true)
+ }
})
- it('does not show the T&M urgency note on a quoted job', async () => {
+ it('does not ask about timesheets on a quoted job', async () => {
const wrapper = mountTab('fixed_price')
await flushPromises()
- expect(wrapper.find('[data-automation-id="JobFinishTab-tm-urgency"]').exists()).toBe(false)
+ expect(item(wrapper, 'timesheets_collected').exists()).toBe(false)
+ for (const key of ITEMS.filter((k) => k !== 'timesheets_collected')) {
+ expect(item(wrapper, key).exists(), key).toBe(true)
+ }
})
it('reflects ticks already recorded on the job', async () => {