@@ -10,11 +10,25 @@ import {
1010 TableHead ,
1111 CircularProgress ,
1212 Collapse ,
13+ Skeleton ,
14+ Link ,
1315} from '@mui/material'
14- import { Contact , Lease , LeaseStatus , PaymentStatus } from '@onecore/types'
15-
16- import { InvoiceWithRows , useContact } from '../hooks/useContact'
1716import { useState } from 'react'
17+ import {
18+ Contact ,
19+ Invoice ,
20+ Lease ,
21+ LeaseStatus ,
22+ PaymentStatus ,
23+ } from '@onecore/types'
24+
25+ import { useContact } from '../hooks/useContact'
26+ import { useInvoicePaymentEvents } from '../hooks/useInvoicePaymentEvents'
27+
28+ const moneyFormatter = new Intl . NumberFormat ( 'sv-SE' , {
29+ minimumFractionDigits : 2 ,
30+ maximumFractionDigits : 2 ,
31+ } )
1832
1933export function ContactCard ( props : { contactCode : string } ) {
2034 const query = useContact ( props . contactCode )
@@ -33,6 +47,12 @@ export function ContactCard(props: { contactCode: string }) {
3347
3448 const { data : contact } = query
3549
50+ const invoices = contact . invoices . sort ( ( a , b ) =>
51+ a . invoiceDate < b . invoiceDate ? 1 : - 1
52+ )
53+
54+ const leases = contact . leases . sort ( ( a , b ) => ( a . status < b . status ? - 1 : 1 ) )
55+
3656 return (
3757 < Box >
3858 < Typography variant = "h2" fontSize = { 24 } >
@@ -46,16 +66,14 @@ export function ContactCard(props: { contactCode: string }) {
4666 { ! contact . leases ?. length ? (
4767 < Typography fontStyle = "italic" > Inga kontrakt hittades</ Typography >
4868 ) : (
49- < Leases
50- leases = { contact . leases . sort ( ( a , b ) => ( a . status < b . status ? - 1 : 1 ) ) }
51- />
69+ < Leases leases = { leases } />
5270 ) }
5371 < Divider />
5472 < Typography variant = "h2" > Fakturor</ Typography >
5573 { ! contact . invoices . length ? (
5674 < Typography fontStyle = "italic" > Inga fakturor hittades</ Typography >
5775 ) : (
58- < Invoices invoices = { contact . invoices } />
76+ < Invoices invoices = { invoices } />
5977 ) }
6078 < Divider />
6179 </ Box >
@@ -130,7 +148,7 @@ function Leases(props: { leases: Lease[] }) {
130148 ) )
131149}
132150
133- function Invoices ( props : { invoices : InvoiceWithRows [ ] } ) {
151+ function Invoices ( props : { invoices : Invoice [ ] } ) {
134152 return (
135153 < Table stickyHeader = { true } sx = { { tableLayout : 'fixed' } } >
136154 < TableHead >
@@ -139,25 +157,20 @@ function Invoices(props: { invoices: InvoiceWithRows[] }) {
139157 < TableCell sx = { { fontWeight : 'bold' } } > Fakturadatum</ TableCell >
140158 < TableCell sx = { { fontWeight : 'bold' } } > Förfallodatum</ TableCell >
141159 < TableCell sx = { { fontWeight : 'bold' } } > Belopp</ TableCell >
142- < TableCell sx = { { fontWeight : 'bold' } } > Referens </ TableCell >
160+ < TableCell sx = { { fontWeight : 'bold' } } > Återstående belopp </ TableCell >
143161 < TableCell sx = { { fontWeight : 'bold' } } > Fakturatyp</ TableCell >
144162 < TableCell sx = { { fontWeight : 'bold' } } > Betalstatus</ TableCell >
145- < TableCell sx = { { fontWeight : 'bold' } } >
146- Skickad till inkasso
147- </ TableCell >
163+ < TableCell sx = { { fontWeight : 'bold' } } > Inkasso</ TableCell >
164+ < TableCell sx = { { fontWeight : 'bold' } } > Källa</ TableCell >
148165 </ TableRow >
149166 </ TableHead >
150167 < TableBody >
151- { Array . from ( props . invoices )
152- . sort ( ( invoice1 , invoice2 ) =>
153- invoice1 . invoiceDate < invoice2 . invoiceDate ? 1 : - 1
154- )
155- . map ( ( invoice ) => (
156- < InvoiceTableRow
157- key = { `${ invoice . invoiceId } -${ invoice . invoiceDate } ` }
158- invoice = { invoice }
159- />
160- ) ) }
168+ { props . invoices . map ( ( invoice ) => (
169+ < InvoiceTableRow
170+ key = { `${ invoice . invoiceId } -${ invoice . invoiceDate } ` }
171+ invoice = { invoice }
172+ />
173+ ) ) }
161174 </ TableBody >
162175 </ Table >
163176 )
@@ -178,7 +191,7 @@ function getStatusName(status: LeaseStatus) {
178191 }
179192}
180193
181- function InvoiceTableRow ( props : { invoice : InvoiceWithRows } ) {
194+ function InvoiceTableRow ( props : { invoice : Invoice } ) {
182195 const { invoice } = props
183196 const [ open , setOpen ] = useState ( false )
184197
@@ -189,6 +202,7 @@ function InvoiceTableRow(props: { invoice: InvoiceWithRows }) {
189202 onClick = { ( ) => setOpen ( ( prev ) => ! prev ) }
190203 sx = { {
191204 cursor : 'pointer' ,
205+ backgroundColor : open ? 'rgba(0, 0, 0, 0.04)' : 'inherit' ,
192206 } }
193207 role = "button"
194208 tabIndex = { 0 }
@@ -207,8 +221,12 @@ function InvoiceTableRow(props: { invoice: InvoiceWithRows }) {
207221 ? yyyymmdd ( new Date ( invoice . expirationDate ) )
208222 : '-' }
209223 </ TableCell >
210- < TableCell > { invoice . amount } </ TableCell >
211- < TableCell > { invoice . reference } </ TableCell >
224+ < TableCell > { moneyFormatter . format ( invoice . amount ) } </ TableCell >
225+ < TableCell >
226+ { invoice . remainingAmount
227+ ? moneyFormatter . format ( invoice . remainingAmount )
228+ : '-' }
229+ </ TableCell >
212230 < TableCell >
213231 { invoice . type === 'Other' ? 'Ströfaktura' : 'Avi' }
214232 </ TableCell >
@@ -218,53 +236,189 @@ function InvoiceTableRow(props: { invoice: InvoiceWithRows }) {
218236 < TableCell >
219237 { invoice . sentToDebtCollection
220238 ? new Date ( invoice . sentToDebtCollection ) . toLocaleDateString ( )
221- : '-' }
239+ : 'Nej' }
240+ </ TableCell >
241+ < TableCell >
242+ { invoice . source === 'legacy' ? 'xpand' : 'xledger' }
222243 </ TableCell >
223244 </ TableRow >
224245 < TableRow >
225- < TableCell style = { { paddingBottom : 0 , paddingTop : 0 } } colSpan = { 6 } >
226- < Collapse in = { open } timeout = { 0 } unmountOnExit >
227- < Box margin = { 1 } >
228- { invoice . type === 'Other' ? (
229- < p > Text: { invoice . description } </ p >
230- ) : (
231- < Table size = "small" >
232- < TableHead >
233- < TableRow >
234- < TableCell sx = { { fontWeight : 'bold' } } >
235- Beskrivning
236- </ TableCell >
237- < TableCell sx = { { fontWeight : 'bold' } } > Belopp</ TableCell >
238- < TableCell sx = { { fontWeight : 'bold' } } > Moms</ TableCell >
239- < TableCell sx = { { fontWeight : 'bold' } } > Totalt</ TableCell >
240- </ TableRow >
241- </ TableHead >
242- < TableBody >
243- { invoice . invoiceRows . map ( ( row , index ) => (
244- < TableRow key = { index } >
245- < TableCell > { row . invoiceRowText } </ TableCell >
246- < TableCell >
247- { row . rowType === 3 ? null : row . amount }
248- </ TableCell >
249- < TableCell >
250- { row . rowType === 3 ? null : row . vat }
251- </ TableCell >
252- < TableCell >
253- { row . rowType === 3 ? null : row . totalAmount }
254- </ TableCell >
255- </ TableRow >
256- ) ) }
257- </ TableBody >
258- </ Table >
259- ) }
260- </ Box >
246+ < TableCell style = { { paddingBottom : 0 , paddingTop : 0 } } colSpan = { 9 } >
247+ < Collapse
248+ in = { open }
249+ timeout = { 0 }
250+ unmountOnExit
251+ sx = { { backgroundColor : open ? 'rgba(0, 0, 0, 0.04)' : 'inherit' } }
252+ >
253+ < InvoiceDetails invoice = { invoice } />
261254 </ Collapse >
262255 </ TableCell >
263256 </ TableRow >
264257 </ >
265258 )
266259}
267260
261+ function InvoiceDetails ( props : { invoice : Invoice } ) {
262+ const { invoice } = props
263+
264+ if ( invoice . type === 'Other' ) {
265+ return (
266+ < Box >
267+ < Typography > Text: { invoice . description } </ Typography >
268+ { invoice . invoiceFileUrl && (
269+ < Link target = "_blank" href = { invoice . invoiceFileUrl } >
270+ Länk till faktura
271+ </ Link >
272+ ) }
273+ </ Box >
274+ )
275+ }
276+
277+ const renderInvoiceRows = ( ) => {
278+ if ( ! invoice . invoiceRows . length ) {
279+ return (
280+ < TableRow >
281+ < TableCell colSpan = { 9 } >
282+ < Typography fontStyle = "italic" >
283+ Inga fakturarader hittades
284+ </ Typography >
285+ </ TableCell >
286+ </ TableRow >
287+ )
288+ }
289+
290+ return invoice . invoiceRows . map ( ( row , index ) => (
291+ < TableRow key = { index } >
292+ < TableCell colSpan = { 2 } > { row . invoiceRowText } </ TableCell >
293+ < TableCell >
294+ { row . rowType === 3 ? null : moneyFormatter . format ( row . amount ) }
295+ </ TableCell >
296+ < TableCell >
297+ { row . rowType === 3 ? null : moneyFormatter . format ( row . deduction ) }
298+ </ TableCell >
299+ < TableCell >
300+ { row . rowType === 3 ? null : moneyFormatter . format ( row . vat ) }
301+ </ TableCell >
302+ < TableCell >
303+ { row . rowType === 3 ? null : moneyFormatter . format ( row . totalAmount ) }
304+ </ TableCell >
305+ </ TableRow >
306+ ) )
307+ }
308+
309+ return (
310+ < Box padding = { 2 } >
311+ < Typography variant = "h2" sx = { { mt : 1 , mb : 1 , fontSize : 18 } } >
312+ Fakturarader
313+ </ Typography >
314+ < Table size = "small" sx = { { tableLayout : 'fixed' } } >
315+ < TableHead >
316+ < TableRow >
317+ < TableCell sx = { { fontWeight : 'bold' } } colSpan = { 2 } >
318+ Beskrivning
319+ </ TableCell >
320+ < TableCell sx = { { fontWeight : 'bold' } } > Belopp</ TableCell >
321+ < TableCell sx = { { fontWeight : 'bold' } } > Avdrag</ TableCell >
322+ < TableCell sx = { { fontWeight : 'bold' } } > Moms</ TableCell >
323+ < TableCell sx = { { fontWeight : 'bold' } } > Totalt</ TableCell >
324+ </ TableRow >
325+ </ TableHead >
326+ < TableBody > { renderInvoiceRows ( ) } </ TableBody >
327+ </ Table >
328+ { invoice . source === 'next' && (
329+ < >
330+ < Typography variant = "h2" sx = { { mt : 2 , mb : 1 , fontSize : 18 } } >
331+ Betalningshändelser
332+ </ Typography >
333+ < InvoicePaymentEvents invoiceId = { invoice . invoiceId } />
334+ </ >
335+ ) }
336+ </ Box >
337+ )
338+ }
339+
340+ function InvoicePaymentEvents ( props : { invoiceId : string } ) {
341+ const eventsQuery = useInvoicePaymentEvents ( props . invoiceId )
342+
343+ const render = ( ) => {
344+ if ( eventsQuery . isLoading ) {
345+ return (
346+ < TableRow >
347+ < TableCell >
348+ < Skeleton variant = "text" width = "40%" height = "25px" />
349+ </ TableCell >
350+ < TableCell >
351+ < Skeleton variant = "text" width = "30%" height = "25px" />
352+ </ TableCell >
353+ < TableCell >
354+ < Skeleton variant = "text" width = "30%" height = "25px" />
355+ </ TableCell >
356+ < TableCell >
357+ < Skeleton variant = "text" width = "30%" height = "25px" />
358+ </ TableCell >
359+ </ TableRow >
360+ )
361+ }
362+
363+ if ( eventsQuery . error ) {
364+ return (
365+ < TableRow >
366+ < TableCell >
367+ < Typography fontStyle = "italic" >
368+ { eventsQuery . error . status === 404
369+ ? 'Inga betalningshändelser hittades'
370+ : 'Ett fel uppstod när betalningshändelser hämtades' }
371+ </ Typography >
372+ </ TableCell >
373+ </ TableRow >
374+ )
375+ }
376+
377+ if ( eventsQuery . data ?. length ) {
378+ return eventsQuery . data . map ( ( event , index ) => (
379+ < TableRow key = { index } >
380+ < TableCell > { event . transactionSourceCode } </ TableCell >
381+ < TableCell > { moneyFormatter . format ( event . amount ) } </ TableCell >
382+ < TableCell > { event . text } </ TableCell >
383+ < TableCell > { yyyymmdd ( new Date ( event . paymentDate ) ) } </ TableCell >
384+ </ TableRow >
385+ ) )
386+ }
387+
388+ return (
389+ < TableRow >
390+ < TableCell >
391+ < Typography fontStyle = "italic" >
392+ Inga betalningshändelser hittades
393+ </ Typography >
394+ </ TableCell >
395+ </ TableRow >
396+ )
397+ }
398+
399+ return (
400+ < Table size = "small" sx = { { tableLayout : 'fixed' } } >
401+ < TableHead >
402+ < TableRow >
403+ < TableCell sx = { { fontWeight : 'bold' , backgroundColor : 'inherit' } } >
404+ Källa
405+ </ TableCell >
406+ < TableCell sx = { { fontWeight : 'bold' , backgroundColor : 'inherit' } } >
407+ Belopp
408+ </ TableCell >
409+ < TableCell sx = { { fontWeight : 'bold' , backgroundColor : 'inherit' } } >
410+ Text
411+ </ TableCell >
412+ < TableCell sx = { { fontWeight : 'bold' , backgroundColor : 'inherit' } } >
413+ Betaldatum
414+ </ TableCell >
415+ </ TableRow >
416+ </ TableHead >
417+ < TableBody > { render ( ) } </ TableBody >
418+ </ Table >
419+ )
420+ }
421+
268422function yyyymmdd ( d : Date ) {
269423 return d . toISOString ( ) . split ( 'T' ) [ 0 ]
270424}
0 commit comments