Skip to content

Commit 53290b1

Browse files
authored
Merge pull request #4046 from bcgov/fix/alex-build-polish-250316
fix: fse redirect when all draft fse set by idir
2 parents 7e763f1 + 0611310 commit 53290b1

File tree

6 files changed

+125
-15
lines changed

6 files changed

+125
-15
lines changed

backend/lcfs/web/api/final_supply_equipment/repo.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,7 @@ async def get_total_kwh_usage_for_report_group(
10051005
Return total kWh usage for effective FSE records in a report group.
10061006
10071007
Effective records are derived by keeping the latest record per
1008-
(charging_equipment_id, charging_equipment_version).
1008+
equipment group_uuid (which spans all versions of the same equipment).
10091009
"""
10101010
conditions = [
10111011
ComplianceReportChargingEquipment.compliance_report_group_uuid
@@ -1016,16 +1016,11 @@ async def get_total_kwh_usage_for_report_group(
10161016

10171017
dedup_subquery = (
10181018
select(
1019-
ComplianceReportChargingEquipment.charging_equipment_id.label(
1020-
"charging_equipment_id"
1021-
),
1022-
ComplianceReportChargingEquipment.charging_equipment_version.label(
1023-
"charging_equipment_version"
1024-
),
1019+
ChargingEquipment.group_uuid.label("equipment_group_uuid"),
10251020
ComplianceReportChargingEquipment.kwh_usage.label("kwh_usage"),
10261021
func.row_number()
10271022
.over(
1028-
partition_by=ComplianceReportChargingEquipment.charging_equipment_id,
1023+
partition_by=ChargingEquipment.group_uuid,
10291024
order_by=(
10301025
desc(ComplianceReportChargingEquipment.charging_equipment_version),
10311026
desc(
@@ -1035,6 +1030,11 @@ async def get_total_kwh_usage_for_report_group(
10351030
)
10361031
.label("row_number"),
10371032
)
1033+
.join(
1034+
ChargingEquipment,
1035+
ChargingEquipment.charging_equipment_id
1036+
== ComplianceReportChargingEquipment.charging_equipment_id,
1037+
)
10381038
.where(and_(*conditions))
10391039
.subquery()
10401040
)

frontend/src/hooks/useChargingSite.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,10 @@ export const useBulkUpdateEquipmentStatus = (options = {}) => {
332332
queryClient.invalidateQueries({
333333
queryKey: ['charging-site-equipment-paginated']
334334
})
335-
// 3. Invalidate all charging sites by organization queries
335+
// 3. Invalidate all charging sites list queries (both IDIR and org)
336+
queryClient.invalidateQueries({
337+
queryKey: ['chargingSitesAll']
338+
})
336339
queryClient.invalidateQueries({
337340
queryKey: ['chargingSitesByOrg']
338341
})

frontend/src/hooks/useFSEProcessing.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export const useFSEProcessing = (siteId) => {
4848
onSuccess: () => {
4949
queryClient.invalidateQueries({ queryKey: ['fse-processing'] })
5050
queryClient.invalidateQueries({ queryKey: ['charging-equipment'] })
51+
// Invalidate charging site list queries so the site disappears if all FSE are now draft
52+
queryClient.invalidateQueries({ queryKey: ['chargingSitesAll'] })
53+
queryClient.invalidateQueries({ queryKey: ['chargingSitesByOrg'] })
54+
queryClient.invalidateQueries({ queryKey: ['chargingSite'] })
5155
}
5256
})
5357

frontend/src/views/ChargingSite/__tests__/components/ChargingSiteFSEGrid.test.jsx

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import { describe, it, expect, vi, beforeEach } from 'vitest'
3-
import { render, screen, fireEvent } from '@testing-library/react'
3+
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
44
import { ChargingSiteFSEGrid } from '../../components/ChargingSiteFSEGrid'
55
import { wrapper } from '@/tests/utils/wrapper.jsx'
66

@@ -30,6 +30,32 @@ vi.mock('@/components/BCDataGrid/BCGridViewer.jsx', () => ({
3030
>
3131
Row Click
3232
</button>
33+
<button
34+
onClick={() =>
35+
props.gridOptions?.onSelectionChanged?.({
36+
api: {
37+
getSelectedNodes: () => [
38+
{ data: { chargingEquipmentId: 2, status: { status: 'Validated' } } }
39+
]
40+
}
41+
})
42+
}
43+
>
44+
Select Validated
45+
</button>
46+
<button
47+
onClick={() =>
48+
props.gridOptions?.onSelectionChanged?.({
49+
api: {
50+
getSelectedNodes: () => [
51+
{ data: { chargingEquipmentId: 3, status: { status: 'Submitted' } } }
52+
]
53+
}
54+
})
55+
}
56+
>
57+
Select Submitted
58+
</button>
3359
</div>
3460
))
3561
}))
@@ -52,6 +78,16 @@ describe('ChargingSiteFSEGrid', () => {
5278
chargingEquipmentId: 1,
5379
status: { status: 'Draft' },
5480
registrationNumber: 'REG001'
81+
},
82+
{
83+
chargingEquipmentId: 2,
84+
status: { status: 'Validated' },
85+
registrationNumber: 'REG002'
86+
},
87+
{
88+
chargingEquipmentId: 3,
89+
status: { status: 'Submitted' },
90+
registrationNumber: 'REG003'
5591
}
5692
],
5793
status: { status: 'Draft' },
@@ -250,4 +286,40 @@ describe('ChargingSiteFSEGrid', () => {
250286
)
251287
})
252288
})
289+
290+
describe('Return to draft button rules', () => {
291+
it('disables Return to draft for IDIR when a Validated row is selected', async () => {
292+
const idirProps = {
293+
...mockProps,
294+
isIDIR: true,
295+
hasAnyRole: vi.fn(() => true),
296+
hasRoles: vi.fn((role) => role === 'analyst')
297+
}
298+
render(<ChargingSiteFSEGrid {...idirProps} />, { wrapper })
299+
300+
fireEvent.click(screen.getByText('Select Validated'))
301+
302+
await waitFor(() => {
303+
expect(
304+
screen.getByRole('button', {
305+
name: 'chargingSite:buttons.returnSelectedToDraft'
306+
})
307+
).toBeDisabled()
308+
})
309+
})
310+
311+
it('keeps Return to draft enabled for non-IDIR when a Validated row is selected', async () => {
312+
render(<ChargingSiteFSEGrid {...mockProps} />, { wrapper })
313+
314+
fireEvent.click(screen.getByText('Select Validated'))
315+
316+
await waitFor(() => {
317+
expect(
318+
screen.getByRole('button', {
319+
name: 'chargingSite:buttons.returnSelectedToDraft'
320+
})
321+
).toBeEnabled()
322+
})
323+
})
324+
})
253325
})

frontend/src/views/ChargingSite/components/ChargingSiteFSEGrid.jsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,18 @@ export const ChargingSiteFSEGrid = ({
7070
return selectedEquipment.every((eq) => eq.status.status === 'Draft' || eq.status.status === 'Updated')
7171
}, [selectedRows, equipmentList])
7272

73-
// Check if selected equipment can be returned to draft (only from Submitted or Validated status)
73+
// Check if selected equipment can be returned to draft (only from Submitted status)
7474
const canReturnToDraft = useMemo(() => {
7575
if (selectedRows.length === 0) return false
7676
const selectedEquipment = equipmentList.filter((eq) =>
7777
selectedRows.includes(eq.chargingEquipmentId)
7878
)
79-
return selectedEquipment.every((eq) =>
80-
eq.status.status === 'Submitted' || eq.status.status === 'Validated'
79+
return selectedEquipment.every((eq) =>
80+
isIDIR
81+
? eq.status.status === 'Submitted'
82+
: eq.status.status === 'Submitted' || eq.status.status === 'Validated'
8183
)
82-
}, [selectedRows, equipmentList])
84+
}, [selectedRows, equipmentList, isIDIR])
8385

8486
// Check if selected equipment can be validated (only from Submitted status)
8587
const canValidate = useMemo(() => {
@@ -195,6 +197,20 @@ export const ChargingSiteFSEGrid = ({
195197
equipmentIds: selectedRows,
196198
newStatus
197199
})
200+
201+
// If moving to Draft, check if all equipment on this site will now be Draft
202+
if (newStatus === 'Draft') {
203+
const allWillBeDraft = equipmentList.every(
204+
(eq) =>
205+
selectedRows.includes(eq.chargingEquipmentId) ||
206+
eq.status.status === 'Draft'
207+
)
208+
if (allWillBeDraft) {
209+
navigate(ROUTES.REPORTS.CHARGING_SITE.INDEX)
210+
return
211+
}
212+
}
213+
198214
refetch()
199215
handleClearFilters()
200216
alertRef.current?.triggerAlert({
@@ -211,7 +227,7 @@ export const ChargingSiteFSEGrid = ({
211227
setModalData(null)
212228
}
213229
},
214-
[selectedRows, siteId, bulkUpdateStatus, handleClearFilters]
230+
[selectedRows, siteId, bulkUpdateStatus, handleClearFilters, equipmentList, navigate]
215231
)
216232

217233
const gridOptions = useMemo(

frontend/src/views/FSEProcessing/FSEProcessing.jsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,21 @@ export const FSEProcessing = () => {
234234
const equipmentIds = selectedRows.map((row) => row.charging_equipment_id)
235235
try {
236236
const result = await returnToDraft(equipmentIds)
237+
238+
// Check if all equipment on this site will now be Draft
239+
let allWillBeDraft = true
240+
gridRef.current?.api?.forEachNode((node) => {
241+
const isDraftTarget = equipmentIds.includes(node.data.charging_equipment_id)
242+
if (!isDraftTarget && node.data.status !== 'Draft') {
243+
allWillBeDraft = false
244+
}
245+
})
246+
247+
if (allWillBeDraft) {
248+
navigate(ROUTES.REPORTS.CHARGING_SITE.INDEX)
249+
return
250+
}
251+
237252
alertRef.current?.triggerAlert({
238253
message: result.message,
239254
severity: 'success'

0 commit comments

Comments
 (0)