1use crate::fields::*;
2use serde::{Deserialize, Serialize};
3use swift_mt_message_macros::{SwiftMessage, serde_swift_fields};
4
5#[serde_swift_fields]
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
91#[validation_rules(MT104_VALIDATION_RULES)]
92pub struct MT104 {
93 #[field("20")]
94 pub field_20: Field20,
95
96 #[field("21R")]
97 pub field_21r: Option<Field21R>,
98
99 #[field("23E")]
100 pub field_23e: Option<Field23E>,
101
102 #[field("21E")]
103 pub field_21e: Option<Field21E>,
104
105 #[field("30")]
106 pub field_30: Field30,
107
108 #[field("51A")]
109 pub field_51a: Option<Field51A>,
110
111 #[field("50#1")]
112 pub field_50_instructing: Option<Field50InstructingParty>,
113
114 #[field("50#2")]
115 pub field_50_creditor: Option<Field50Creditor>,
116
117 #[field("52")]
118 pub field_52: Option<Field52CreditorBank>,
119
120 #[field("26T")]
121 pub field_26t: Option<Field26T>,
122
123 #[field("77B")]
124 pub field_77b: Option<Field77B>,
125
126 #[field("71A")]
127 pub field_71a: Option<Field71A>,
128
129 #[field("72")]
130 pub field_72: Option<Field72>,
131
132 #[field("#")]
133 pub transactions: Vec<MT104Transaction>,
134
135 #[field("32B")]
136 pub field_32b: Option<Field32B>,
137
138 #[field("19")]
139 pub field_19: Option<Field19>,
140
141 #[field("71F")]
142 pub field_71f: Option<Field71F>,
143
144 #[field("71G")]
145 pub field_71g: Option<Field71G>,
146
147 #[field("53")]
148 pub field_53: Option<Field53SenderCorrespondent>,
149}
150
151#[serde_swift_fields]
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, SwiftMessage)]
153pub struct MT104Transaction {
154 #[field("21")]
155 pub field_21: Field21NoOption,
156
157 #[field("23E")]
158 pub field_23e: Option<Field23E>,
159
160 #[field("21C")]
161 pub field_21c: Option<Field21C>,
162
163 #[field("21D")]
164 pub field_21d: Option<Field21D>,
165
166 #[field("21E")]
167 pub field_21e: Option<Field21E>,
168
169 #[field("32B")]
170 pub field_32b: Field32B,
171
172 #[field("50#1")]
173 pub field_50_instructing: Option<Field50InstructingParty>,
174
175 #[field("50#2")]
176 pub field_50_creditor: Option<Field50Creditor>,
177
178 #[field("52")]
179 pub field_52: Option<Field52CreditorBank>,
180
181 #[field("57")]
182 pub field_57: Option<Field57DebtorBank>,
183
184 #[field("59")]
185 pub field_59: Field59Debtor,
186
187 #[field("70")]
188 pub field_70: Option<Field70>,
189
190 #[field("26T")]
191 pub field_26t: Option<Field26T>,
192
193 #[field("77B")]
194 pub field_77b: Option<Field77B>,
195
196 #[field("33B")]
197 pub field_33b: Option<Field33B>,
198
199 #[field("71A")]
200 pub field_71a: Option<Field71A>,
201
202 #[field("71F")]
203 pub field_71f: Option<Field71F>,
204
205 #[field("71G")]
206 pub field_71g: Option<Field71G>,
207
208 #[field("36")]
209 pub field_36: Option<Field36>,
210}
211
212const MT104_VALIDATION_RULES: &str = r#"{
214 "rules": [
215 {
216 "id": "C1",
217 "description": "If field 23E is present in sequence A and contains RFDD then field 23E must be present in all occurrences of sequence B. If field 23E is present in sequence A and does not contain RFDD then field 23E must not be present in any occurrence of sequence B. If field 23E is not present in sequence A then field 23E must be present in all occurrences of sequence B",
218 "condition": {
219 "if": [
220 {"exists": ["fields", "23E"]},
221 {
222 "if": [
223 {"==": [{"var": "fields.23E.instruction_code"}, "RFDD"]},
224 {
225 "all": [
226 {"var": "fields.#"},
227 {"var": "23E"}
228 ]
229 },
230 {
231 "all": [
232 {"var": "fields.#"},
233 {"!": {"var": "23E"}}
234 ]
235 }
236 ]
237 },
238 {
239 "all": [
240 {"var": "fields.#"},
241 {"var": "23E"}
242 ]
243 }
244 ]
245 }
246 },
247 {
248 "id": "C2",
249 "description": "Field 50a (option A or K) must be present in either sequence A or in each occurrence of sequence B, but must never be present in both sequences",
250 "condition": {
251 "or": [
252 {
253 "and": [
254 {"or": [
255 {"exists": ["fields", "50", "A"]},
256 {"exists": ["fields", "50", "K"]}
257 ]},
258 {
259 "all": [
260 {"var": "fields.#"},
261 {
262 "and": [
263 {"!": {"var": "50#2.A"}},
264 {"!": {"var": "50#2.K"}}
265 ]
266 }
267 ]
268 }
269 ]
270 },
271 {
272 "and": [
273 {
274 "and": [
275 {"!": {"exists": ["fields", "50", "A"]}},
276 {"!": {"exists": ["fields", "50", "K"]}}
277 ]
278 },
279 {
280 "all": [
281 {"var": "fields.#"},
282 {
283 "or": [
284 {"var": "50#2.A"},
285 {"var": "50#2.K"}
286 ]
287 }
288 ]
289 }
290 ]
291 }
292 ]
293 }
294 },
295 {
296 "id": "C3",
297 "description": "When present in sequence A, fields 21E, 26T, 52a, 71A, 77B and 50a (option C or L) must not be present in any occurrence of sequence B",
298 "condition": {
299 "and": [
300 {
301 "if": [
302 {"exists": ["fields", "21E"]},
303 {
304 "all": [
305 {"var": "fields.#"},
306 {"!": {"exists": ["fields", "21E"]}}
307 ]
308 },
309 true
310 ]
311 },
312 {
313 "if": [
314 {"exists": ["fields", "26T"]},
315 {
316 "all": [
317 {"var": "fields.#"},
318 {"!": {"exists": ["fields", "26T"]}}
319 ]
320 },
321 true
322 ]
323 },
324 {
325 "if": [
326 {"exists": ["fields", "52"]},
327 {
328 "all": [
329 {"var": "fields.#"},
330 {"!": {"exists": ["fields", "52"]}}
331 ]
332 },
333 true
334 ]
335 },
336 {
337 "if": [
338 {"exists": ["fields", "71A"]},
339 {
340 "all": [
341 {"var": "fields.#"},
342 {"!": {"exists": ["fields", "71A"]}}
343 ]
344 },
345 true
346 ]
347 },
348 {
349 "if": [
350 {"exists": ["fields", "77B"]},
351 {
352 "all": [
353 {"var": "fields.#"},
354 {"!": {"exists": ["fields", "77B"]}}
355 ]
356 },
357 true
358 ]
359 },
360 {
361 "if": [
362 {"or": [
363 {"exists": ["fields", "50#1", "C"]},
364 {"exists": ["fields", "50#1", "L"]}
365 ]},
366 {
367 "all": [
368 {"var": "fields.#"},
369 {
370 "and": [
371 {"!": {"exists": ["fields", "50#1", "C"]}},
372 {"!": {"exists": ["fields", "50#1", "L"]}}
373 ]
374 }
375 ]
376 },
377 true
378 ]
379 }
380 ]
381 }
382 },
383 {
384 "id": "C4",
385 "description": "If field 21E is present in sequence A, field 50a (option A or K) must also be present in sequence A. In each occurrence of sequence B, if field 21E is present, then field 50a (option A or K) must also be present",
386 "condition": {
387 "and": [
388 {
389 "if": [
390 {"exists": ["fields", "21E"]},
391 {
392 "or": [
393 {"exists": ["fields", "50#2", "A"]},
394 {"exists": ["fields", "50#2", "K"]}
395 ]
396 },
397 true
398 ]
399 },
400 {
401 "all": [
402 {"var": "fields.#"},
403 {
404 "if": [
405 {"exists": ["fields", "21E"]},
406 {
407 "or": [
408 {"exists": ["fields", "50#2", "A"]},
409 {"exists": ["fields", "50#2", "K"]}
410 ]
411 },
412 true
413 ]
414 }
415 ]
416 }
417 ]
418 }
419 },
420 {
421 "id": "C5",
422 "description": "In sequence A, if field 23E is present and contains RTND then field 72 must be present",
423 "condition": {
424 "if": [
425 {"and": [
426 {"exists": ["fields", "23E"]},
427 {"==": [{"var": "fields.23E.instruction_code"}, "RTND"]}
428 ]},
429 {"exists": ["fields", "72"]},
430 {
431 "if": [
432 {"exists": ["fields", "23E"]},
433 {"!": {"exists": ["fields", "72"]}},
434 {"!": {"exists": ["fields", "72"]}}
435 ]
436 }
437 ]
438 }
439 },
440 {
441 "id": "C6",
442 "description": "If field 71F is present in one or more occurrence of sequence B, then it must also be present in sequence C, and vice-versa. Same for field 71G",
443 "condition": {
444 "and": [
445 {
446 "if": [
447 {
448 "some": [
449 {"var": "fields.#"},
450 {"exists": ["fields", "71F"]}
451 ]
452 },
453 {"exists": ["fields", "71F"]},
454 {
455 "if": [
456 {"exists": ["fields", "71F"]},
457 {
458 "some": [
459 {"var": "fields.#"},
460 {"exists": ["fields", "71F"]}
461 ]
462 },
463 true
464 ]
465 }
466 ]
467 },
468 {
469 "if": [
470 {
471 "some": [
472 {"var": "fields.#"},
473 {"exists": ["fields", "71G"]}
474 ]
475 },
476 {"exists": ["fields", "71G"]},
477 {
478 "if": [
479 {"exists": ["fields", "71G"]},
480 {
481 "some": [
482 {"var": "fields.#"},
483 {"exists": ["fields", "71G"]}
484 ]
485 },
486 true
487 ]
488 }
489 ]
490 }
491 ]
492 }
493 },
494 {
495 "id": "C7",
496 "description": "In each occurrence of sequence B, if field 33B is present then the currency code or the amount, or both, must be different between fields 33B and 32B",
497 "condition": {
498 "all": [
499 {"var": "fields.#"},
500 {
501 "if": [
502 {"exists": ["fields", "33B"]},
503 {
504 "or": [
505 {"!=": [{"var": "33B.currency"}, {"var": "32B.currency"}]},
506 {"!=": [{"var": "33B.amount"}, {"var": "32B.amount"}]}
507 ]
508 },
509 true
510 ]
511 }
512 ]
513 }
514 },
515 {
516 "id": "C8",
517 "description": "In any occurrence of sequence B, if field 33B is present and the currency codes in fields 32B and 33B are different, then field 36 must be present",
518 "condition": {
519 "all": [
520 {"var": "fields.#"},
521 {
522 "if": [
523 {"exists": ["fields", "33B"]},
524 {
525 "if": [
526 {"!=": [{"var": "33B.currency"}, {"var": "32B.currency"}]},
527 {"exists": ["fields", "36"]},
528 {
529 "if": [
530 {"==": [{"var": "33B.currency"}, {"var": "32B.currency"}]},
531 {"!": {"exists": ["fields", "36"]}},
532 true
533 ]
534 }
535 ]
536 },
537 true
538 ]
539 }
540 ]
541 }
542 },
543 {
544 "id": "C9",
545 "description": "If sequence C is present and if the amount in field 32B of sequence C is equal to the sum of the amounts of the fields 32B of sequence B, then field 19 must not be present",
546 "condition": {
547 "if": [
548 {"exists": ["fields", "32B"]},
549 {
550 "if": [
551 {"==": [
552 {"var": "fields.32B.amount"},
553 {
554 "reduce": [
555 {"var": "fields.#"},
556 {"+": [{"var": "accumulator"}, {"var": "current.32B.amount"}]},
557 0
558 ]
559 }
560 ]},
561 {"!": {"exists": ["fields", "19"]}},
562 {"exists": ["fields", "19"]}
563 ]
564 },
565 true
566 ]
567 }
568 },
569 {
570 "id": "C10",
571 "description": "If field 19 is present in sequence C then it must be equal to the sum of the amounts in all occurrences of field 32B in sequence B",
572 "condition": {
573 "if": [
574 {"exists": ["fields", "19"]},
575 {"==": [
576 {"var": "fields.19.amount"},
577 {
578 "reduce": [
579 {"var": "fields.#"},
580 {"+": [{"var": "accumulator"}, {"var": "current.32B.amount"}]},
581 0
582 ]
583 }
584 ]},
585 true
586 ]
587 }
588 },
589 {
590 "id": "C11",
591 "description": "The currency code in fields 32B and 71G in sequences B and C must be the same for all occurrences. The currency code in fields 71F must be the same for all occurrences",
592 "condition": true
593 },
594 {
595 "id": "C12",
596 "description": "If field 23E in sequence A contains RFDD, then field 21R is optional, fields in sequence B are restricted, and sequence C must not be present",
597 "condition": {
598 "if": [
599 {"and": [
600 {"exists": ["fields", "23E"]},
601 {"==": [{"var": "fields.23E.instruction_code"}, "RFDD"]}
602 ]},
603 {
604 "and": [
605 {
606 "all": [
607 {"var": "fields.#"},
608 {
609 "and": [
610 {"!": {"exists": ["fields", "21E"]}},
611 {"!": {"exists": ["fields", "50#2", "A"]}},
612 {"!": {"exists": ["fields", "50#2", "K"]}},
613 {"!": {"exists": ["fields", "52"]}},
614 {"!": {"exists": ["fields", "71F"]}},
615 {"!": {"exists": ["fields", "71G"]}}
616 ]
617 }
618 ]
619 },
620 {"!": {"exists": ["fields", "32B"]}},
621 {"!": {"exists": ["fields", "19"]}},
622 {"!": {"exists": ["fields", "71F"]}},
623 {"!": {"exists": ["fields", "71G"]}},
624 {"!": {"exists": ["fields", "53"]}}
625 ]
626 },
627 {
628 "and": [
629 {"!": {"exists": ["fields", "21R"]}},
630 {
631 "or": [
632 {"exists": ["fields", "32B"]},
633 {"exists": ["fields", "19"]},
634 {"exists": ["fields", "71F"]},
635 {"exists": ["fields", "71G"]},
636 {"exists": ["fields", "53"]}
637 ]
638 }
639 ]
640 }
641 ]
642 }
643 },
644 {
645 "id": "C13",
646 "description": "If field 23E in sequence A is present and contains RFDD, then field 119 of User Header must be present and contain RFDD",
647 "condition": {
648 "if": [
649 {"and": [
650 {"exists": ["fields", "23E"]},
651 {"==": [{"var": "fields.23E.instruction_code"}, "RFDD"]}
652 ]},
653 {"and": [
654 {"exists": ["user_header", "validation_flag"]},
655 {"==": [{"var": "user_header.validation_flag"}, "RFDD"]}
656 ]},
657 {"!": {"exists": ["user_header", "validation_flag"]}}
658 ]
659 }
660 },
661 {
662 "id": "TRANSACTION_MINIMUM",
663 "description": "At least one transaction must be present in sequence B",
664 "condition": true
665 },
666 {
667 "id": "CURRENCY_CODE_VALIDATION",
668 "description": "All currency codes must be valid ISO 4217 3-letter codes",
669 "condition": {
670 "and": [
671 {
672 "all": [
673 {"var": "fields.#"},
674 {
675 "and": [
676 {"!=": [{"var": "32B.currency"}, ""]},
677 {
678 "if": [
679 {"exists": ["fields", "33B"]},
680 {"!=": [{"var": "33B.currency"}, ""]},
681 true
682 ]
683 },
684 {
685 "if": [
686 {"exists": ["fields", "71F"]},
687 {"!=": [{"var": "71F.currency"}, ""]},
688 true
689 ]
690 },
691 {
692 "if": [
693 {"exists": ["fields", "71G"]},
694 {"!=": [{"var": "71G.currency"}, ""]},
695 true
696 ]
697 }
698 ]
699 }
700 ]
701 },
702 {
703 "if": [
704 {"exists": ["fields", "32B"]},
705 {"!=": [{"var": "fields.32B.currency"}, ""]},
706 true
707 ]
708 },
709 {
710 "if": [
711 {"exists": ["fields", "71F"]},
712 {"!=": [{"var": "fields.71F.currency"}, ""]},
713 true
714 ]
715 },
716 {
717 "if": [
718 {"exists": ["fields", "71G"]},
719 {"!=": [{"var": "fields.71G.currency"}, ""]},
720 true
721 ]
722 }
723 ]
724 }
725 },
726 {
727 "id": "AMOUNT_CONSISTENCY",
728 "description": "All amounts must be properly formatted",
729 "condition": {
730 "and": [
731 {
732 "all": [
733 {"var": "fields.#"},
734 {
735 "and": [
736 {">": [{"var": "32B.amount"}, -1]},
737 {
738 "if": [
739 {"exists": ["fields", "33B"]},
740 {">": [{"var": "33B.amount"}, -1]},
741 true
742 ]
743 },
744 {
745 "if": [
746 {"exists": ["fields", "71F"]},
747 {">": [{"var": "71F.amount"}, -1]},
748 true
749 ]
750 },
751 {
752 "if": [
753 {"exists": ["fields", "71G"]},
754 {">": [{"var": "71G.amount"}, -1]},
755 true
756 ]
757 }
758 ]
759 }
760 ]
761 },
762 {
763 "if": [
764 {"exists": ["fields", "32B"]},
765 {">": [{"var": "fields.32B.amount"}, -1]},
766 true
767 ]
768 },
769 {
770 "if": [
771 {"exists": ["fields", "19"]},
772 {">": [{"var": "fields.19.amount"}, -1]},
773 true
774 ]
775 },
776 {
777 "if": [
778 {"exists": ["fields", "71F"]},
779 {">": [{"var": "fields.71F.amount"}, -1]},
780 true
781 ]
782 },
783 {
784 "if": [
785 {"exists": ["fields", "71G"]},
786 {">": [{"var": "fields.71G.amount"}, -1]},
787 true
788 ]
789 }
790 ]
791 }
792 },
793 {
794 "id": "REFERENCE_FORMAT",
795 "description": "Reference fields must not contain invalid patterns",
796 "condition": {
797 "and": [
798 {"!=": [{"var": "fields.20.reference"}, ""]},
799 {"!": {"in": ["//", {"var": "fields.20.reference"}]}},
800 {
801 "all": [
802 {"var": "fields.#"},
803 {
804 "and": [
805 {"!=": [{"var": "21.reference"}, ""]},
806 {"!": {"in": ["//", {"var": "21.reference"}]}}
807 ]
808 }
809 ]
810 }
811 ]
812 }
813 },
814 {
815 "id": "EXECUTION_DATE",
816 "description": "Requested execution date must be valid",
817 "condition": {
818 "and": [
819 {"exists": ["fields", "30"]},
820 {"!=": [{"var": "fields.30.execution_date"}, ""]}
821 ]
822 }
823 }
824 ],
825 "constants": {
826 "VALID_CHARGE_CODES": ["OUR", "SHA", "BEN"],
827 "VALID_INSTRUCTION_CODES_MT104": ["RFDD", "RTND", "SDVA", "INTC", "CORT"]
828 }
829}"#;