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 ActionId 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 ActionId]
48 { workDate = "2025-11-06"
50 , workAction = Action_Organization
51 , workScope = ["NGIpkgs"]
52 , workReferences = ["https://meet.google.com/hii-druc-tjp"]
53 , workDescription = "first meeting"
56 { workDate = "2025-11-06"
58 , workAction = Action_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 = Action_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 = Action_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 = Action_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 = Action_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 = Action_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 = Action_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 = Action_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 = Action_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 = Action_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 = Action_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 = Action_Development
147 , workScope = ["NGIpkgs", "Bonfire"]
148 , workReferences = []
149 , workDescription = "add updateScript and fix opencv"
152 { workDate = "2025-11-19"
154 , workAction = Action_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 = Action_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 = Action_Organization
171 , workScope = ["NGIpkgs"]
172 , workReferences = []
173 , workDescription = "weekly meeting"
176 { workDate = "2025-11-21"
178 , workAction = Action_Development
179 , workScope = ["NGIpkgs", "Bonfire"]
180 , workReferences = []
181 , workDescription = "continue to package assets"
184 { workDate = "2025-11-23"
186 , workAction = Action_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 = Action_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 = Action_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 = Action_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 = Action_Development
219 , workScope = ["NGIpkgs", "Bonfire"]
220 , workReferences = []
221 , workDescription = "begin the service module"
224 { workDate = "2025-12-02"
226 , workAction = Action_Development
227 , workScope = ["NGIpkgs", "Bonfire"]
228 , workReferences = []
229 , workDescription = "continue the service module"
232 { workDate = "2025-12-03"
234 , workAction = Action_Development
235 , workScope = ["NGIpkgs", "Bonfire"]
236 , workReferences = []
237 , workDescription = "first working service module"
240 { workDate = "2025-12-04"
242 , workAction = Action_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 = Action_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 = Action_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 = Action_Documentation
267 , workScope = ["NGIpkgs", "Manuals"]
268 , workReferences = []
269 , workDescription = "prepare wednesday meeting"
272 { workDate = "2025-12-07"
274 , workAction = Action_Documentation
275 , workScope = ["NGIpkgs", "Manuals"]
276 , workReferences = []
277 , workDescription = "prepare demo for wednesday meeting"
280 { workDate = "2025-12-08"
282 , workAction = Action_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 = Action_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 = Action_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 = Action_Organization
307 , workScope = ["NGIpkgs"]
308 , workReferences = []
309 , workDescription = "weekly visio meeting"
312 { workDate = "2025-12-11"
314 , workAction = Action_Documentation
315 , workScope = ["NGIpkgs", "Manuals"]
316 , workReferences = []
317 , workDescription = "continue to document"
320 { workDate = "2025-12-12"
322 , workAction = Action_Documentation
323 , workScope = ["NGIpkgs", "Manuals"]
324 , workReferences = []
325 , workDescription = "continue to document"
328 { workDate = "2025-12-13"
330 , workAction = Action_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 = Action_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 = Action_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 = Action_Development
362 , workScope = ["NGIpkgs", "Bonfire"]
363 , workReferences = []
364 , workDescription = "overcoming bugs, again"
367 { workDate = "2025-12-16"
369 , workAction = Action_Review
370 , workScope = ["NGIpkgs"]
371 , workReferences = []
372 , workDescription = "visio to review PRs"
375 { workDate = "2025-12-16"
377 , workAction = Action_Review
378 , workScope = ["NGIpkgs"]
379 , workReferences = []
380 , workDescription = "visio to review PRs"
383 { workDate = "2025-12-17"
385 , workAction = Action_Review
386 , workScope = ["NGIpkgs"]
387 , workReferences = []
388 , workDescription = "visio to review PRs"
391 { workDate = "2025-12-18"
393 , workAction = Action_Review
394 , workScope = ["NGIpkgs", "dnsvizor"]
395 , workReferences = []
396 , workDescription = "review linj’s PR"
399 { workDate = "2025-12-19"
401 , workAction = Action_Organization
402 , workScope = ["NGIpkgs"]
403 , workReferences = []
404 , workDescription = "weekly meeting"
407 { workDate = "2025-12-21"
409 , workAction = Action_Development
410 , workScope = ["NGIpkgs", "Bonfire"]
411 , workReferences = []
414 , "__structuredAttrs" & Doc.InlineCode
418 { workDate = "2025-12-23"
420 , workAction = Action_Documentation
421 , workScope = ["NGIpkgs", "Manuals"]
422 , workReferences = []
423 , workDescription = "improve building the manuals"
426 { workDate = "2025-12-23"
428 , workAction = Action_Development
429 , workScope = ["NGIpkgs", "Bonfire"]
430 , workReferences = []
431 , workDescription = "address reviewers’ concerns"
434 { workDate = "2025-12-24"
436 , workAction = Action_Development
437 , workScope = ["NGIpkgs", "Bonfire"]
438 , workReferences = []
439 , workDescription = "update Bonfire and report issues upstream"
442 { workDate = "2025-12-24"
444 , workAction = Action_Documentation
445 , workScope = ["NGIpkgs", "Manuals"]
446 , workReferences = []
447 , workDescription = "document"
450 { workDate = "2025-12-24"
452 , workAction = Action_Development
453 , workScope = ["NGIpkgs", "Bonfire"]
454 , workReferences = []
455 , workDescription = "update Bonfire to fix bugs"
458 { workDate = "2025-12-25"
460 , workAction = Action_Documentation
461 , workScope = ["NGIpkgs", "Manuals"]
462 , workReferences = []
463 , workDescription = "document"
466 { workDate = "2025-12-25"
468 , workAction = Action_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 = Action_Development
477 , workScope = ["NGIpkgs", "Bonfire"]
478 , workReferences = []
479 , workDescription = "update to fix bugs"
482 { workDate = "2025-12-28"
484 , workAction = Action_Documentation
485 , workScope = ["NGIpkgs", "Manuals"]
486 , workReferences = []
487 , workDescription = "document"
490 { workDate = "2025-12-29"
492 , workAction = Action_Development
493 , workScope = ["NGIpkgs", "Bonfire"]
494 , workReferences = []
495 , workDescription = "add more tests"
498 { workDate = "2025-12-29"
500 , workAction = Action_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 = Action_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 = Action_Development
517 , workScope = ["NGIpkgs", "Bonfire"]
518 , workReferences = []
519 , workDescription = "support other flavours"
522 { workDate = "2026-01-03"
524 , workAction = Action_Development
525 , workScope = ["NGIpkgs", "Bonfire"]
526 , workReferences = []
527 , workDescription = "support other flavours, and make update more resilient"
530 { workDate = "2026-01-04"
532 , workAction = Action_Development
533 , workScope = ["NGIpkgs", "Bonfire"]
534 , workReferences = []
535 , workDescription = "provide upstream with reproducers"
538 { workDate = "2026-01-05"
540 , workAction = Action_Development
541 , workScope = ["NGIpkgs", "Bonfire"]
542 , workReferences = []
543 , workDescription = "update to latest version"
546 { workDate = "2026-01-07"
548 , workAction = Action_Development
549 , workScope = ["NGIpkgs", "Bonfire"]
550 , workReferences = []
551 , workDescription = "fix updating"
554 { workDate = "2026-01-07"
556 , workAction = Action_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 = Action_Documentation
565 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
566 , workReferences = []
567 , workDescription = "address reviewer’s comments"
570 { workDate = "2026-01-09"
572 , workAction = Action_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 = Action_Development
581 , workScope = ["NGIpkgs", "dnsvizor"]
582 , workReferences = []
583 , workDescription = ["replace ", Doc.InlineCode "hillingar", " to package MirageOS unikernels"]
586 { workDate = "2026-01-09"
588 , workAction = Action_Organization
589 , workScope = ["NGIpkgs"]
590 , workReferences = []
591 , workDescription = "weekly meeting"
594 { workDate = "2026-01-12"
596 , workAction = Action_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 = Action_Documentation
605 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
606 , workReferences = []
607 , workDescription = "render options"
610 { workDate = "2026-01-13"
612 , workAction = Action_Documentation
613 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
614 , workReferences = []
615 , workDescription = "update"
618 { workDate = "2026-01-16"
620 , workAction = Action_Organization
621 , workScope = ["NGIpkgs"]
622 , workReferences = []
623 , workDescription = "weekly meeting"
626 { workDate = "2026-01-19"
628 , workAction = Action_Documentation
629 , workScope = ["NGIpkgs", "Manuals"] -- goes into NGI Core
630 , workReferences = []
631 , workDescription = "split into several PDF"
634 { workDate = "2026-01-22"
636 , workAction = Action_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 = Action_Organization
645 , workScope = ["NGIpkgs"]
646 , workReferences = []
647 , workDescription = "weekly meeting"
650 { workDate = "2026-01-25"
652 , workAction = Action_Development
653 , workScope = ["NGIpkgs", "Seppo"]
654 , workReferences = []
655 , workDescription = "begin packaging"
658 { workDate = "2026-01-26"
660 , workAction = Action_Development
661 , workScope = ["NGIpkgs", "Seppo"]
662 , workReferences = []
663 , workDescription = "continue packaging"
666 { workDate = "2026-01-27"
668 , workAction = Action_Development
669 , workScope = ["NGIpkgs", "Seppo"]
670 , workReferences = []
671 , workDescription = "continue packaging"
680 deriving (Eq, Ord, Show, Generic, Enum, NFData)
682 invoiceRate_NGIpkgs =
683 [ Action_Organization := 31.25
684 , Action_Development := 31.25
685 , Action_Documentation := 31.25
686 , Action_Review := 31.25
690 = Action_Organization
692 | Action_Documentation
694 deriving (Eq, Ord, Show, Generic, Enum, NFData)
696 instance Doc.ToInline ActionId where
698 Action_Development -> "development"
699 Action_Documentation -> "documentation"
700 Action_Organization -> "organization"
701 Action_Review -> "review"
703 instance Get (Invoice ActionId Orga.EntityId InvoiceId) InvoiceId where
704 get invoiceId = case invoiceId of
708 , invoiceType = InvoiceTypeSale
711 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
712 , invoiceIssuer = Orga.EntityJulmInfo
713 , invoiceRecipient = Orga.EntityJulmInfo
714 , invoiceRates = [Action_Development := 0]
716 [ InvoiceMentionTVANonApplicable
717 , InvoiceMentionIndemnitéForfaitaire
718 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
722 { workDate = "2000-01-01"
724 , workAction = Action_Development
726 , workReferences = []
727 , workDescription = ""
732 invoiceEmittedOn = "2026-01-22"
736 , invoiceType = InvoiceTypeSale
737 , invoiceOrders = ["Contract n°2025-24"]
739 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
740 , invoiceIssuer = Orga.EntityJulmInfo
741 , invoiceRecipient = Orga.EntityNixOSFoundationNGITeam
742 , invoiceRates = invoiceRate_NGIpkgs
744 [ InvoiceMentionTVANonApplicable
745 , InvoiceMentionIndemnitéForfaitaire
746 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
750 & List.filter \work ->
753 [ work & workDate & (>= "2025-11-06")
754 , work & workDate & (<= "2025-12-22")
755 , work & workScope & (/= ["NGIpkgs", "Manuals"])
759 invoiceEmittedOn = "2026-01-22"
763 , invoiceType = InvoiceTypeSale
764 , invoiceOrders = ["contract n°2026-03"]
766 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
767 , invoiceIssuer = Orga.EntityJulmInfo
768 , invoiceRecipient = Orga.EntityNixOSFoundationNGITeam
769 , invoiceRates = invoiceRate_NGIpkgs
771 [ InvoiceMentionTVANonApplicable
772 , InvoiceMentionIndemnitéForfaitaire
773 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
777 & List.filter \work ->
779 [ work & workDate & (>= "2025-12-23")
780 , work & workDate & (<= "2026-12-31")
781 , work & workScope & (/= ["NGIpkgs", "Manuals"])
782 , work & workScope & (/= ["NGIpkgs", "Funkwhale"])
785 [ work & workDate & (>= "2026-01-09")
786 , work & workDate & (<= "2026-01-28")
787 , work & workAction & (== Action_Organization)
788 , work & workScope & (== ["NGIpkgs"])
795 invoiceEmittedOn = "2026-01-27"
799 , invoiceType = InvoiceTypeSale
800 , invoiceOrders = ["contract n°2026-FixMe"]
802 , invoicePaymentDueBefore = invoiceEmittedOn & Time.addLocalTime (31 * Time.nominalDay)
803 , invoiceIssuer = Orga.EntityJulmInfo
804 , invoiceRecipient = Orga.EntityNixOSFoundationNGITeam
805 , invoiceRates = invoiceRate_NGIpkgs
807 [ InvoiceMentionTVANonApplicable
808 , InvoiceMentionIndemnitéForfaitaire
809 , InvoiceMentionIndemnitéTaux (12.15 :: Amount 100 (UnitName "%"))
813 & List.filter \work ->
816 [ work & workDate & (>= "2026-01-21")
817 , work & workDate & (<= "2026-12-31")
818 , work & workScope & (== ["NGIpkgs", "Manuals"])
822 [ work & workDate & (>= "2025-12-29")
823 , work & workDate & (<= "2026-12-31")
824 , work & workScope & (== ["NGIpkgs", "Funkwhale"])
828 [ work & workDate & (>= "2026-01-09")
829 , work & workDate & (<= "2026-01-28")
830 , work & workAction & (== Action_Organization)
831 , work & workScope & (== ["NGIpkgs"])
838 invoiceEmittedOn = "2026-01-28"
840 invoices :: [InvoiceId]