1 {-# LANGUAGE DeriveAnyClass #-}
2 {-# LANGUAGE OverloadedLists #-}
3 {-# LANGUAGE OverloadedStrings #-}
5 module Tests.Invoice where
7 import Data.List qualified as List
8 import Data.Text.Lazy qualified as Text.Lazy
9 import Data.Time.Clock qualified as Time
10 import Data.Time.LocalTime qualified as Time
11 import Test.Syd (Spec, describe, goldenByteStringBuilderFile, it)
12 import Tests.Organization qualified as Orga
13 import Tests.Utils.Tests (goldenPath)
14 import Text.Blaze.Renderer.Text qualified as Blaze.Text
15 import Text.Blaze.Renderer.Utf8 qualified as Blaze
18 import Literate.Accounting (Amount, Unit (UnitName))
19 import Literate.Database
20 import Literate.Document qualified as Doc
21 import Literate.Document.HTML qualified as HTML
22 import Literate.Invoice
23 import Literate.Invoice.HTML (invoiceIdInline)
24 import Literate.Invoice.HTML qualified as HTML
25 import Literate.Prelude
27 spec :: HasCallStack => Spec
29 -- aroundAll readDicts do
31 forM_ invoices \invId -> do
33 inv = invId & get @(Invoice Orga.EntityId InvoiceId)
38 & Blaze.Text.renderMarkup
40 outPath <- goldenPath idS "html"
42 goldenByteStringBuilderFile outPath do
43 inv & HTML.htmlIO <&> Blaze.renderMarkupBuilder
45 works_NGIpkgs :: [Work]
48 { workDate = "2025-11-06"
50 , workAction = ["organization"]
51 , workScope = ["NGIpkgs"]
52 , workReferences = ["https://meet.google.com/hii-druc-tjp"]
53 , workDescription = "first meeting"
56 { workDate = "2025-11-06"
58 , workAction = ["development"]
59 , workScope = ["NGIpkgs", "Bonfire"]
60 , workReferences = ["https://github.com/ju1m/bonfire-app/commits/nix"]
61 , workDescription = "focus on the hardest part first"
64 { workDate = "2025-11-07"
66 , workAction = ["development"]
67 , workScope = ["NGIpkgs", "Bonfire"]
68 , workReferences = ["https://github.com/ju1m/bonfire-app/commits/nix"]
69 , workDescription = "continuing to package Bonfire deps"
72 { workDate = "2025-11-08"
74 , workAction = ["development"]
75 , workScope = ["Nixpkgs", "opencv"]
76 , workReferences = ["https://github.com/NixOS/nixpkgs/pull/459592"]
77 , workDescription = "fix opencv in nixpkgs"
80 { workDate = "2025-11-09"
82 , workAction = ["development"]
83 , workScope = ["NGIpkgs", "Bonfire"]
84 , workReferences = ["https://github.com/ju1m/bonfire-app/commits/nix"]
85 , workDescription = "continue to fix deps"
88 { workDate = "2025-11-09"
90 , workAction = ["development"]
91 , workScope = ["NGIpkgs", "Bonfire"]
92 , workReferences = ["https://github.com/ju1m/bonfire-app/commits/nix"]
93 , workDescription = "continue to fix deps"
96 { workDate = "2025-11-11"
98 , workAction = ["development"]
99 , workScope = ["NGIpkgs", "Bonfire"]
100 , workReferences = ["https://github.com/ju1m/bonfire-app/commits/nix"]
101 , workDescription = "continue to fix deps"
104 { workDate = "2025-11-12"
106 , workAction = ["development"]
107 , workScope = ["NGIpkgs", "Bonfire"]
108 , workReferences = ["https://github.com/ju1m/bonfire-app/commits/nix"]
109 , workDescription = "continue to fix deps"
112 { workDate = "2025-11-14"
114 , workAction = ["development"]
115 , workScope = ["NGIpkgs", "Bonfire"]
116 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1812"]
117 , workDescription = "continue to fix deps and move into ngipkgs"
120 { workDate = "2025-11-14"
122 , workAction = ["organization"]
123 , workScope = ["NGIpkgs"]
124 , workReferences = ["https://www.notion.so/nixos-foundation/Nix-NGI-weekly-review-34-2ab59d49e1be80939503c655b009e934"]
125 , workDescription = "weekly meeting"
128 { workDate = "2025-11-15"
130 , workAction = ["development"]
131 , workScope = ["NGIpkgs", "Bonfire"]
132 , workReferences = []
133 , workDescription = "finish to build Bonfire and start to fix runtime bugs"
136 { workDate = "2025-11-16"
138 , workAction = ["development"]
139 , workScope = ["NGIpkgs", "Bonfire"]
140 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1812#issuecomment-3537609170"]
141 , workDescription = "workaround and contribute upstream"
144 { workDate = "2025-11-17"
146 , workAction = ["development"]
147 , workScope = ["NGIpkgs", "Bonfire"]
148 , workReferences = []
149 , workDescription = "add updateScript and fix opencv"
152 { workDate = "2025-11-19"
154 , workAction = ["development"]
155 , workScope = ["NGIpkgs", "Bonfire"]
156 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1812#issuecomment-3554376221"]
157 , workDescription = "continue to fix startup crashes"
160 { workDate = "2025-11-21"
162 , workAction = ["development"]
163 , workScope = ["NGIpkgs", "Bonfire"]
164 , workReferences = []
165 , workDescription = "begin to package yarn assets. Upstream likes convoluted code and fake libraries."
168 { workDate = "2025-11-21"
170 , workAction = ["organization"]
171 , workScope = ["NGIpkgs"]
172 , workReferences = []
173 , workDescription = "weekly meeting"
176 { workDate = "2025-11-21"
178 , workAction = ["development"]
179 , workScope = ["NGIpkgs", "Bonfire"]
180 , workReferences = []
181 , workDescription = "continue to package assets"
184 { workDate = "2025-11-23"
186 , workAction = ["development"]
187 , workScope = ["NGIpkgs", "Bonfire"]
188 , workReferences = []
189 , workDescription = "continue to package assets and try to get Bonfire to work"
192 { workDate = "2025-11-24"
194 , workAction = ["development"]
195 , workScope = ["NGIpkgs", "Bonfire"]
196 , workReferences = ["https://github.com/surface-ui/surface/issues/762#issuecomment-3577030748"]
197 , workDescription = "continue to solve problems with Bonfire packaging"
200 { workDate = "2025-11-27"
202 , workAction = ["development"]
203 , workScope = ["NGIpkgs", "Bonfire"]
204 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1812#issuecomment-3584319056"]
205 , workDescription = "successfully auto-update and build Bonfire"
208 { workDate = "2025-11-28"
210 , workAction = ["organization"]
211 , workScope = ["NGIpkgs"]
212 , workReferences = ["https://www.notion.so/nixos-foundation/Nix-NGI-weekly-review-36-2b959d49e1be80fa823cd4d83581fda5#2b959d49e1be80fc85eed38c9a9dca86"]
213 , workDescription = "weekly meeting"
216 { workDate = "2025-11-29"
218 , workAction = ["development"]
219 , workScope = ["NGIpkgs", "Bonfire"]
220 , workReferences = []
221 , workDescription = "begin the service module"
224 { workDate = "2025-12-02"
226 , workAction = ["development"]
227 , workScope = ["NGIpkgs", "Bonfire"]
228 , workReferences = []
229 , workDescription = "continue the service module"
232 { workDate = "2025-12-03"
234 , workAction = ["development"]
235 , workScope = ["NGIpkgs", "Bonfire"]
236 , workReferences = []
237 , workDescription = "first working service module"
240 { workDate = "2025-12-04"
242 , workAction = ["development"]
243 , workScope = ["NGIpkgs", "Bonfire"]
244 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1871"]
245 , workDescription = "improve service module"
248 { workDate = "2025-12-05"
250 , workAction = ["organization"]
251 , workScope = ["NGIpkgs"]
252 , workReferences = ["https://www.notion.so/nixos-foundation/Nix-NGI-weekly-review-36-2b959d49e1be80fa823cd4d83581fda5"]
253 , workDescription = "weekly meeting"
256 { workDate = "2025-12-05"
258 , workAction = ["documentation"]
259 , workScope = ["NGIpkgs", "Manuals"]
260 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1954"]
261 , workDescription = "meeting"
264 { workDate = "2025-12-06"
266 , workAction = ["documentation"]
267 , workScope = ["NGIpkgs", "Manuals"]
268 , workReferences = []
269 , workDescription = "prepare wednesday meeting"
272 { workDate = "2025-12-07"
274 , workAction = ["documentation"]
275 , workScope = ["NGIpkgs", "Manuals"]
276 , workReferences = []
277 , workDescription = "prepare demo for wednesday meeting"
280 { workDate = "2025-12-08"
282 , workAction = ["documentation"]
283 , workScope = ["NGIpkgs", "Manuals"]
284 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1891"]
285 , workDescription = "prepare demo for wednesday meeting"
288 { workDate = "2025-12-09"
290 , workAction = ["documentation"]
291 , workScope = ["NGIpkgs", "Manuals"]
292 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1891"]
293 , workDescription = "continue to document"
296 { workDate = "2025-12-10"
298 , workAction = ["review"]
299 , workScope = ["NGIpkgs"]
300 , workReferences = ["https://www.notion.so/nixos-foundation/Nix-NGI-best-practices-for-NixOS-modules-implementation-2c559d49e1be80a8a499f21abb203d6f"]
301 , workDescription = "visio on “best practices”"
304 { workDate = "2025-12-12"
306 , workAction = ["organization"]
307 , workScope = ["NGIpkgs"]
308 , workReferences = []
309 , workDescription = "weekly visio meeting"
312 { workDate = "2025-12-11"
314 , workAction = ["documentation"]
315 , workScope = ["NGIpkgs", "Manuals"]
316 , workReferences = []
317 , workDescription = "continue to document"
320 { workDate = "2025-12-12"
322 , workAction = ["documentation"]
323 , workScope = ["NGIpkgs", "Manuals"]
324 , workReferences = []
325 , workDescription = "continue to document"
328 { workDate = "2025-12-13"
330 , workAction = ["documentation"]
331 , workScope = ["NGIpkgs", "Manuals"]
332 , workReferences = []
334 [ "setup sphinx like "
336 { Doc.inlineLinkTarget = "https://nix.dev"
337 , Doc.inlineLinkText = "nix.dev"
343 { workDate = "2025-12-13"
345 , workAction = ["development"]
346 , workScope = ["NGIpkgs", "Bonfire"]
347 , workReferences = ["https://github.com/bonfire-networks/bonfire-app/issues/1670#issuecomment-3650762914"]
348 , workDescription = "update to latest; overcoming new bugs introduced by upstream"
351 { workDate = "2025-12-15"
353 , workAction = ["development"]
354 , workScope = ["NGIpkgs", "Bonfire"]
355 , workReferences = []
356 , workDescription = "improving the update script and still overcoming upstream bugs"
359 { workDate = "2025-12-16"
361 , workAction = ["development"]
362 , workScope = ["NGIpkgs", "Bonfire"]
363 , workReferences = []
364 , workDescription = "overcoming bugs, again"
367 { workDate = "2025-12-16"
369 , workAction = ["review"]
370 , workScope = ["NGIpkgs"]
371 , workReferences = []
372 , workDescription = "visio to review PRs"
375 { workDate = "2025-12-16"
377 , workAction = ["review"]
378 , workScope = ["NGIpkgs"]
379 , workReferences = []
380 , workDescription = "visio to review PRs"
383 { workDate = "2025-12-17"
385 , workAction = ["review"]
386 , workScope = ["NGIpkgs"]
387 , workReferences = []
388 , workDescription = "visio to review PRs"
391 { workDate = "2025-12-18"
393 , workAction = ["review"]
394 , workScope = ["NGIpkgs", "dnsvizor"]
395 , workReferences = []
396 , workDescription = "review linj’s PR"
399 { workDate = "2025-12-19"
401 , workAction = ["organization"]
402 , workScope = ["NGIpkgs"]
403 , workReferences = []
404 , workDescription = "weekly meeting"
407 { workDate = "2025-12-21"
409 , workAction = ["development"]
410 , workScope = ["NGIpkgs", "Bonfire"]
411 , workReferences = []
414 , "__structuredAttrs" & Doc.InlineCode
418 { workDate = "2025-12-23"
420 , workAction = ["documentation"]
421 , workScope = ["NGIpkgs", "Manuals"]
422 , workReferences = []
423 , workDescription = "improve building the manuals"
426 { workDate = "2025-12-23"
428 , workAction = ["development"]
429 , workScope = ["NGIpkgs", "Bonfire"]
430 , workReferences = []
431 , workDescription = "address reviewers’ concerns"
434 { workDate = "2025-12-24"
436 , workAction = ["development"]
437 , workScope = ["NGIpkgs", "Bonfire"]
438 , workReferences = []
439 , workDescription = "update Bonfire and report issues upstream"
442 { workDate = "2025-12-24"
444 , workAction = ["documentation"]
445 , workScope = ["NGIpkgs", "Manuals"]
446 , workReferences = []
447 , workDescription = "document"
450 { workDate = "2025-12-24"
452 , workAction = ["development"]
453 , workScope = ["NGIpkgs", "Bonfire"]
454 , workReferences = []
455 , workDescription = "update Bonfire to fix bugs"
458 { workDate = "2025-12-25"
460 , workAction = ["documentation"]
461 , workScope = ["NGIpkgs", "Manuals"]
462 , workReferences = []
463 , workDescription = "document"
466 { workDate = "2025-12-25"
468 , workAction = ["development"]
469 , workScope = ["NGIpkgs", "Bonfire"]
470 , workReferences = ["https://github.com/bonfire-networks/bonfire-app/issues/1698#issuecomment-3692147409"]
471 , workDescription = "update and fix bugs"
474 { workDate = "2025-12-26"
476 , workAction = ["development"]
477 , workScope = ["NGIpkgs", "Bonfire"]
478 , workReferences = []
479 , workDescription = "update to fix bugs"
482 { workDate = "2025-12-28"
484 , workAction = ["documentation"]
485 , workScope = ["NGIpkgs", "Manuals"]
486 , workReferences = []
487 , workDescription = "document"
490 { workDate = "2025-12-29"
492 , workAction = ["development"]
493 , workScope = ["NGIpkgs", "Bonfire"]
494 , workReferences = []
495 , workDescription = "add more tests"
498 { workDate = "2025-12-29"
500 , workAction = ["review"]
501 , workScope = ["NGIpkgs", "Funkwhale"] -- goes into NGI Review
502 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1707#discussion_r2651644145"]
503 , workDescription = ""
506 { workDate = "2025-12-30"
508 , workAction = ["development"]
509 , workScope = ["NGIpkgs", "BEAM"]
510 , workReferences = []
511 , workDescription = ["improve ", Doc.InlineCode "buildMix", " & ", Doc.InlineCode "mixRelease", " for packaging Elixir software"]
514 { workDate = "2026-01-01"
516 , workAction = ["development"]
517 , workScope = ["NGIpkgs", "Bonfire"]
518 , workReferences = []
519 , workDescription = "support other flavours"
522 { workDate = "2026-01-03"
524 , workAction = ["development"]
525 , workScope = ["NGIpkgs", "Bonfire"]
526 , workReferences = []
527 , workDescription = "support other flavours, and make update more resilient"
530 { workDate = "2026-01-04"
532 , workAction = ["development"]
533 , workScope = ["NGIpkgs", "Bonfire"]
534 , workReferences = []
535 , workDescription = "provide upstream with reproducers"
538 { workDate = "2026-01-05"
540 , workAction = ["development"]
541 , workScope = ["NGIpkgs", "Bonfire"]
542 , workReferences = []
543 , workDescription = "update to latest version"
546 { workDate = "2026-01-07"
548 , workAction = ["development"]
549 , workScope = ["NGIpkgs", "Bonfire"]
550 , workReferences = []
551 , workDescription = "fix updating"
554 { workDate = "2026-01-07"
556 , workAction = ["documentation"]
557 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
558 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/1891"]
559 , workDescription = "address reviewer’s comment"
562 { workDate = "2026-01-09"
564 , workAction = ["documentation"]
565 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
566 , workReferences = []
567 , workDescription = "address reviewer’s comments"
570 { workDate = "2026-01-09"
572 , workAction = ["development"]
573 , workScope = ["NGIpkgs", "Bonfire"]
574 , workReferences = ["https://github.com/bonfire-networks/bonfire-app/issues/1730"]
575 , workDescription = "answer upstream’s questions"
578 { workDate = "2026-01-11"
580 , workAction = ["development"]
581 , workScope = ["NGIpkgs", "dnsvizor"]
582 , workReferences = []
583 , workDescription = ["replace ", Doc.InlineCode "hillingar", " to package MirageOS unikernels"]
586 { workDate = "2026-01-09"
588 , workAction = ["organization"]
589 , workScope = ["NGIpkgs"]
590 , workReferences = []
591 , workDescription = "weekly meeting"
594 { workDate = "2026-01-12"
596 , workAction = ["development"]
597 , workScope = ["NGIpkgs", "dnsvizor"]
598 , workReferences = ["https://github.com/ju1m/ngipkgs/commits/dnsvizor/"]
599 , workDescription = ["remove the need for ", Doc.InlineCode "--allow-import-from-derivation"]
602 { workDate = "2026-01-12"
604 , workAction = ["documentation"]
605 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
606 , workReferences = []
607 , workDescription = "render options"
610 { workDate = "2026-01-13"
612 , workAction = ["documentation"]
613 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
614 , workReferences = []
615 , workDescription = "update"
618 { workDate = "2026-01-16"
620 , workAction = ["organization"]
621 , workScope = ["NGIpkgs"]
622 , workReferences = []
623 , workDescription = "weekly meeting"
626 { workDate = "2026-01-19"
628 , workAction = ["documentation"]
629 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
630 , workReferences = []
631 , workDescription = "split into several PDF"
634 { workDate = "2026-01-22"
636 , workAction = ["development"]
637 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
638 , workReferences = ["https://github.com/ngi-nix/ngipkgs/pull/2010"]
639 , workDescription = "address reviewers’ concerns"
642 { workDate = "2026-01-23"
644 , workAction = ["organization"]
645 , workScope = ["NGIpkgs"]
646 , workReferences = []
647 , workDescription = "weekly meeting"
650 { workDate = "2026-01-25"
652 , workAction = ["development"]
653 , workScope = ["NGIpkgs", "Seppo"]
654 , workReferences = []
655 , workDescription = "begin packaging"
658 { workDate = "2026-01-26"
660 , workAction = ["development"]
661 , workScope = ["NGIpkgs", "Seppo"]
662 , workReferences = []
663 , workDescription = "continue packaging"
666 { workDate = "2026-01-27"
668 , workAction = ["development"]
669 , workScope = ["NGIpkgs", "Seppo"]
670 , workReferences = []
671 , workDescription = "continue packaging"
680 deriving (Eq, Ord, Show, Generic, Enum, NFData)
682 invoiceRate_NGIpkgs =
683 [ ["organization"] := 31.25
684 , ["development"] := 31.25
685 , ["documentation"] := 31.25
686 , ["review"] := 31.25
689 instance Get (Invoice Orga.EntityId InvoiceId) InvoiceId where
690 get invoiceId = case invoiceId of
694 , invoiceType = InvoiceTypeSale
697 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
698 , invoiceIssuer = Orga.EntityJulmInfo
699 , invoiceRecipient = Orga.EntityJulmInfo
700 , invoiceRates = [["development"] := 0]
702 [ InvoiceMentionTVANonApplicable
703 , InvoiceMentionIndemnitéForfaitaire
704 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
708 { workDate = "2000-01-01"
710 , workAction = ["development"]
712 , workReferences = []
713 , workDescription = ""
718 invoiceEmittedOn = "2026-01-22"
722 , invoiceType = InvoiceTypeSale
723 , invoiceOrders = ["Contract n°2025-24"]
725 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
726 , invoiceIssuer = Orga.EntityJulmInfo
727 , invoiceRecipient = Orga.EntityNixOSFoundationNGITeam
728 , invoiceRates = invoiceRate_NGIpkgs
730 [ InvoiceMentionTVANonApplicable
731 , InvoiceMentionIndemnitéForfaitaire
732 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
736 & List.filter \work ->
739 [ work & workDate & (>= "2025-11-06")
740 , work & workDate & (<= "2025-12-22")
741 , work & workScope & (/= ["NGIpkgs", "Manuals"])
745 invoiceEmittedOn = "2026-01-22"
749 , invoiceType = InvoiceTypeSale
750 , invoiceOrders = ["contract n°2026-03"]
752 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
753 , invoiceIssuer = Orga.EntityJulmInfo
754 , invoiceRecipient = Orga.EntityNixOSFoundationNGITeam
755 , invoiceRates = invoiceRate_NGIpkgs
757 [ InvoiceMentionTVANonApplicable
758 , InvoiceMentionIndemnitéForfaitaire
759 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
763 & List.filter \work ->
765 [ work & workDate & (>= "2025-12-23")
766 , work & workDate & (<= "2026-12-31")
767 , work & workScope & (/= ["NGIpkgs", "Manuals"])
768 , work & workScope & (/= ["NGIpkgs", "Funkwhale"])
771 [ work & workDate & (>= "2026-01-09")
772 , work & workDate & (<= "2026-01-28")
773 , work & workAction & (== ["organization"])
774 , work & workScope & (== ["NGIpkgs"])
781 invoiceEmittedOn = "2026-01-27"
785 , invoiceType = InvoiceTypeSale
786 , invoiceOrders = ["contract n°2026-FixMe"]
788 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
789 , invoiceIssuer = Orga.EntityJulmInfo
790 , invoiceRecipient = Orga.EntityNixOSFoundationNGITeam
791 , invoiceRates = invoiceRate_NGIpkgs
793 [ InvoiceMentionTVANonApplicable
794 , InvoiceMentionIndemnitéForfaitaire
795 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
799 & List.filter \work ->
802 [ work & workDate & (>= "2026-01-21")
803 , work & workDate & (<= "2026-12-31")
804 , work & workScope & (== ["NGIpkgs", "Manuals"])
808 [ work & workDate & (>= "2025-12-29")
809 , work & workDate & (<= "2026-12-31")
810 , work & workScope & (== ["NGIpkgs", "Funkwhale"])
814 [ work & workDate & (>= "2026-01-09")
815 , work & workDate & (<= "2026-01-28")
816 , work & workAction & (== ["organization"])
817 , work & workScope & (== ["NGIpkgs"])
824 invoiceEmittedOn = "2026-01-28"
826 invoices :: [InvoiceId]