Compare commits

...

1237 Commits

Author SHA1 Message Date
AzureAD\SylvainDUVERNAY
b3e8dc9c33 Merge branch 'main' of https://gitea.open-squared.tech/admin/tradon 2026-07-14 16:13:24 +02:00
AzureAD\SylvainDUVERNAY
fab46dde2f Import Prices (All Sources) 2026-07-14 16:13:21 +02:00
3dfbb25281 Certification 2026-07-14 15:30:04 +02:00
eb38977dca Bug lot.qt 2026-07-14 13:47:40 +02:00
ac3198ee70 Bug lot.qt 2026-07-14 13:29:44 +02:00
cf4bd94d03 Bug lot.qt 2026-07-14 12:02:07 +02:00
07ee8b4f99 Bug lot.qt 2026-07-14 11:39:39 +02:00
7fc134f7a6 Bug lot.qt 2026-07-14 11:26:18 +02:00
e4590e6177 Polish linkage report table spacing 2026-07-14 10:41:50 +02:00
5a644d430b Refine linkage report spacing 2026-07-14 09:54:29 +02:00
AzureAD\SylvainDUVERNAY
808e75daa6 New file import format (ARGUS +ICIS) 2026-07-13 21:35:01 +02:00
AzureAD\SylvainDUVERNAY
febe29c6b4 Merge branch 'main' of https://gitea.open-squared.tech/admin/tradon 2026-07-13 20:53:00 +02:00
AzureAD\SylvainDUVERNAY
b312ea9bd3 add folder temp_documents with Excel file mapping used for price import 2026-07-13 20:52:52 +02:00
3321830b5e Improve linkage report layout 2026-07-13 19:20:03 +02:00
f2fe626d72 Add mid price value field 2026-07-13 16:52:53 +02:00
ddd88cd840 Add SOF calculation overrides 2026-07-12 13:10:13 +02:00
47d0f025b3 Support multiple laytime start candidates 2026-07-12 11:52:28 +02:00
8719a63fa5 Adjust purchase PnL graph diagnostics 2026-07-10 14:49:53 +02:00
27b865184c Show purchase PnL graph values 2026-07-10 13:26:01 +02:00
c2a523430e Fix purchase PnL graph data source 2026-07-10 12:52:23 +02:00
8d386e6754 Pnl graph 2026-07-10 12:22:16 +02:00
62eed772fc Demurrage 2026-07-09 17:31:49 +02:00
b0bcaf293d Demurrage 2026-07-09 17:27:01 +02:00
9cf1bc3604 Demurrage 2026-07-09 17:13:41 +02:00
858b7258d9 Pnl graph 2026-07-08 14:38:36 +02:00
3122255bc7 Bug invoice rate ICT 2026-07-07 20:55:47 +02:00
5c3464da5c Linkage report 2026-07-07 18:47:15 +02:00
82143ba87d Linkage report 2026-07-07 18:42:34 +02:00
1b292e4594 Linkage report 2026-07-07 18:07:42 +02:00
0f4edc16ce Linkage template 2026-07-07 17:45:59 +02:00
7ac1e118ae Linkage template 2026-07-07 16:58:55 +02:00
94fd181c11 Linkage template 2026-07-07 16:37:53 +02:00
4ea701d669 Linkage report 2026-07-07 15:58:59 +02:00
b3b14039e0 Linkage 2026-07-07 15:05:04 +02:00
ae39ff2f7d Melya template 2026-07-07 08:40:18 +02:00
2ffd99b03a Melya template 2026-07-06 12:26:48 +02:00
7d5ac651ab Melya template 2026-07-06 11:56:14 +02:00
e8b3fe5bc4 Custom duplicate bug 2026-07-03 11:31:27 +02:00
ea85927a17 Pnl clean up 2026-07-01 15:39:46 +02:00
99d5d2a849 Price composition + Pnl open matched 2026-06-30 18:27:40 +02:00
c0ac8fac68 Coffee 2026-06-29 23:05:27 +02:00
2743a7bc14 Coffee 2026-06-29 22:50:38 +02:00
cd49703879 Coffee 2026-06-29 22:43:03 +02:00
cc4a48c38c Coffee 2026-06-29 22:31:21 +02:00
6fedb92b1a Coffee 2026-06-29 22:24:51 +02:00
cad7f7235e Coffee 2026-06-29 21:58:11 +02:00
b08d2f4dab Coffee 2026-06-29 21:36:04 +02:00
2a30b03cbe Coffee 2026-06-29 21:32:19 +02:00
eb244b1e3e Coffee 2026-06-29 21:26:09 +02:00
f2947c9f86 Coffee 2026-06-29 21:16:42 +02:00
f9ade37eab Coffee 2026-06-29 20:34:23 +02:00
5635856711 Show line action domain filters 2026-06-28 15:48:12 +02:00
edc20fa546 ITSA points 28.06 2026-06-28 15:26:28 +02:00
d4f9d02cb4 ITSA points 28.06 2026-06-28 14:18:06 +02:00
780f3dcbdb ITSA points 28.06 2026-06-28 14:11:45 +02:00
d5c6d5c4b5 ITSA points 28.06 2026-06-28 13:51:10 +02:00
104d5c616c Purchase & Sale Lines 2026-06-28 13:43:26 +02:00
f45cdb0807 ITSA points 28.06 2026-06-28 13:31:48 +02:00
b7b54c8b7b Invoice Melya template 2026-06-26 14:28:26 +02:00
c9dceb9c06 Lot invoicing + Fee padding 2026-06-23 16:06:17 +02:00
cf7aa7f5b5 Lot invoicing + Fee padding 2026-06-23 15:28:11 +02:00
2998d8d570 Lot invoicing + Fee padding 2026-06-23 15:08:49 +02:00
83b879d200 Lot invoicing + Fee padding 2026-06-23 14:31:52 +02:00
50b262dbd2 Lot invoicing + Fee padding 2026-06-23 13:54:14 +02:00
fbf3842e6c Lot invoicing + fee padding 2026-06-23 13:12:37 +02:00
ef8d914820 Lot invoicing + Fee 2026-06-23 12:18:13 +02:00
423d9476fc Padding logic in fee 2026-06-23 09:21:10 +02:00
d08510f084 Lot invocing + Fee 2026-06-22 17:53:53 +02:00
ed818c2287 Lot invoicing + Fee invoicing 2026-06-22 17:44:55 +02:00
703868001b Fee report 2026-06-22 17:16:27 +02:00
6af3de0ffb Add physical lot + Link 2026-06-22 16:14:05 +02:00
95dba725a8 Add physical lot + Link 2026-06-22 16:00:55 +02:00
a880535bc4 Add physical + Link 2026-06-22 15:33:43 +02:00
8f676c2396 Lots Management add user and date 2026-06-22 13:15:55 +02:00
dfbc006efb Pnl bug sh fee 2026-06-19 12:39:58 +02:00
4f331d5c8e DN/CN commission 2026-06-19 09:21:52 +02:00
227a6c14a8 Commssion DN/CN 2026-06-19 08:34:29 +02:00
2b0381c2ae Fee qt state 2026-06-18 21:11:46 +02:00
2a1512a436 Duplicate from LM 2026-06-18 13:51:05 +02:00
1c20181d06 Duplicate custom 2026-06-18 13:33:41 +02:00
6c2061e779 Duplicate custom 2026-06-18 13:20:20 +02:00
1f24646227 Duplicate custom 2026-06-18 13:15:41 +02:00
2d737b2628 Duplicate custom 2026-06-18 13:05:34 +02:00
903b42f730 Duplicate custom 2026-06-18 12:59:16 +02:00
f8d53a27fa Duplicate custom 2026-06-18 11:54:01 +02:00
4b5e49f264 Duplicate custom 2026-06-18 11:34:19 +02:00
3b3f9c0988 Duplicate purchase 2026-06-18 11:12:56 +02:00
248e410d88 Scenario Mtm 2026-06-18 10:56:03 +02:00
a754e15e32 Mtm bug 2026-06-17 12:25:21 +02:00
ee183e23c5 Fee report 2026-06-17 11:56:25 +02:00
8441bf98e7 Fee report 2026-06-17 11:25:33 +02:00
02e5c43f3d Commission CN/DN 2026-06-17 11:04:43 +02:00
6ba5361269 CN/DN 2026-06-17 10:52:29 +02:00
f0714d5670 CN/DN 2026-06-17 10:23:13 +02:00
b8bfcc4308 CN/DN 2026-06-17 09:59:16 +02:00
c268c3b460 CN/DN 2026-06-17 09:46:57 +02:00
04b3c1d924 DN/CN 2026-06-17 09:41:57 +02:00
d8012bd2da CN/DN 2026-06-17 09:32:20 +02:00
dd2acc2d09 CN/DN commission 2026-06-17 09:19:21 +02:00
a8102d513f Fee CN/DN 2026-06-17 00:08:52 +02:00
ee1cb3e7fb Fee CN/DN 2026-06-16 23:47:35 +02:00
d0dd8965cd ICT template 2026-06-16 20:38:57 +02:00
0833baa2ab Fee linked curr 2026-06-16 19:53:29 +02:00
8eba0cd066 Pricing linked 2026-06-16 15:35:12 +02:00
38af948374 ICT template 2026-06-16 14:58:13 +02:00
36ee92cbb2 Pricing cron 2026-06-16 13:01:10 +02:00
fdf855f7db Pricing adj 2026-06-16 11:26:23 +02:00
31f617ae1c Bug Party 2026-06-16 10:40:34 +02:00
07e48944b6 Melya PO 2026-06-15 20:04:37 +02:00
3a0edea8cd ICT template 2026-06-15 18:14:42 +02:00
9b7ccf1feb Broker DN/CN ICT 2026-06-15 15:06:46 +02:00
dd8e3e3056 Dashboard bug 2026-06-15 13:31:26 +02:00
d8a0dd43cf Template ICT 2026-06-15 12:53:28 +02:00
7b427813be Fee linked unit 2026-06-15 11:31:09 +02:00
d705d86e80 Pricing Before/After 2026-06-15 08:28:59 +02:00
667b66487d Pricing adj 2026-06-14 17:29:28 +02:00
9c92904b3e Pricing adj 2026-06-14 09:48:39 +02:00
8a60a8e67f Matching price 2026-06-13 17:37:32 +02:00
e6de614507 Duplicate + open form 2026-06-13 16:48:45 +02:00
a7c061f52d Duplicate 2026-06-13 15:41:45 +02:00
e4e2087463 Duplicate custom 2026-06-13 15:21:36 +02:00
c15b13cb91 ITSA template 2026-06-13 14:57:30 +02:00
e981f96cad ITSA proforma 2026-06-13 14:13:41 +02:00
aac7bad852 Template bug 2026-06-13 12:25:49 +02:00
1e4d9d6e17 Sale bug 2026-06-13 12:16:52 +02:00
a0363fa9c7 Sale bug 2026-06-13 12:10:14 +02:00
e19dcafcb7 ITSA proforma 2026-06-13 11:58:27 +02:00
6a5a267cfd Unlink open when Add physical lot 2026-06-13 11:37:48 +02:00
5c9f437945 Lots Management 2026-06-13 11:25:57 +02:00
ec90d19cc7 Delivery period changed into Shipment period 2026-06-13 11:04:13 +02:00
77b1ab333b Remove physical lot for Planned 2026-06-13 10:38:19 +02:00
1f3e32fbe8 Pricing cron 2026-06-13 10:00:45 +02:00
a435631289 Valuation currency 2026-06-12 20:46:09 +02:00
1a5e2e4033 Mtm new 2026-06-12 20:34:47 +02:00
7107625664 Valuation fee 2026-06-12 19:39:16 +02:00
dbfd6a0d89 Valuation 2026-06-12 19:09:08 +02:00
6ac19a1334 Pricing default 2026-06-11 14:31:35 +02:00
855dc3a4dd Pricing default 2026-06-11 14:03:02 +02:00
52459a267b Pricing manual 2026-06-11 10:49:05 +02:00
9d13cfd587 Mtm bug 2026-06-10 15:20:35 +02:00
de8310891b Bug Valuation 2026-06-10 14:13:48 +02:00
925fdfd32b Shipment Pnl 2026-06-10 11:37:33 +02:00
96ad6dd1c6 Pnl shipment 2026-06-10 10:57:34 +02:00
68bd105e59 Template Melya 2026-06-09 14:32:01 +02:00
90f64cf55f Template 2026-06-09 14:27:58 +02:00
f722c6fa1c Template 2026-06-09 14:20:32 +02:00
217344d3d5 Template 2026-06-09 14:13:05 +02:00
5cb4317742 Template Melya 2026-06-09 14:08:33 +02:00
cf3ad4bb31 Template 2026-06-09 14:04:36 +02:00
fcc5c3866a Confirmed 2026-06-09 14:00:22 +02:00
f59150d1a9 Melya template + Confirmed 2026-06-09 13:43:21 +02:00
67cb5491e2 Melya template 2026-06-09 13:30:24 +02:00
734c0c8a34 Melya template 2026-06-09 11:12:23 +02:00
fd04810f0c Confirmed button 2026-06-09 10:35:27 +02:00
c8e8e4f866 Stop workflow to Confirmed 2026-06-08 23:12:17 +02:00
3a6f426033 Sale design 2026-06-08 21:44:08 +02:00
5838d848c2 Purchase design 2026-06-08 21:02:57 +02:00
79c701b711 Purchase design 2026-06-08 20:54:58 +02:00
c36c6a411f Purchase design 2026-06-08 20:47:22 +02:00
745325eb20 Purchase design 2026-06-08 20:33:28 +02:00
dbd5e5b77f Purchase design 2026-06-08 20:18:52 +02:00
c2f3d3995f Purchase design 2026-06-08 19:52:14 +02:00
02a15c93de Purchase design 2026-06-08 18:53:44 +02:00
abbcdfd2ec Purchase design 2026-06-08 18:44:14 +02:00
dc067fb812 Purchase design 2026-06-08 18:27:06 +02:00
8691ba430f Purchase header design 2026-06-08 18:10:22 +02:00
e2e0e8cece Purchase form design 2026-06-08 17:55:51 +02:00
d182b0eceb LM header 2026-06-08 14:04:52 +02:00
74d1465470 Pricing form 2026-06-07 21:43:37 +02:00
1abfbbbc3d Pricing form 2026-06-07 21:33:13 +02:00
72501ab625 Pricing date 2026-06-07 21:25:23 +02:00
65e9f4d7d6 BL nb stock move 2026-06-07 21:13:31 +02:00
0a72076346 PL SL design 2026-06-07 18:10:47 +02:00
3977e138e2 PL design 2026-06-07 17:56:50 +02:00
45acff11bc PL SL design 2026-06-07 17:39:16 +02:00
1b15ab4c69 PL SL design 2026-06-07 13:37:41 +02:00
7735306f9c PL SL design 2026-06-07 13:27:19 +02:00
76b34ba280 PL SL design 2026-06-07 13:17:44 +02:00
eb82211da7 PL design 2026-06-07 12:54:15 +02:00
c2495bb6ea PL design 2026-06-07 11:54:52 +02:00
9e5cd39629 Purchase line design 2026-06-07 11:48:32 +02:00
05a90f64a5 Purchase line design 2026-06-07 11:32:27 +02:00
4d3c5e26e2 Tab order 2026-06-07 11:11:05 +02:00
6ac1904a0c Planned 2026-06-07 10:16:15 +02:00
8e82986bd5 Planned 2026-06-07 09:57:57 +02:00
6ffc159255 Planned 2026-06-07 09:47:55 +02:00
4ebfbe6ddd Planned 2026-06-07 09:37:32 +02:00
700adb87f9 Link to transport 2026-06-07 07:53:18 +02:00
126225ea6a Link to transport 2026-06-07 07:41:33 +02:00
1339f9b2f1 Link to transport 2026-06-07 07:30:42 +02:00
b3b7803313 Link to transport 2026-06-07 07:23:32 +02:00
24b4e00080 Default From To 2026-06-07 07:02:18 +02:00
846c2d6a93 Planned status 2026-06-07 06:22:15 +02:00
801e53f953 Shipment creation bug 2026-06-07 05:45:21 +02:00
06f9a62dbc LM header 2026-06-06 21:23:03 +02:00
eceeca997e LM header 2026-06-06 21:16:17 +02:00
f37d21c761 LM header 2026-06-06 21:12:03 +02:00
1b97899091 LM header 2026-06-06 21:07:54 +02:00
afee4243cc Lots management header 2026-06-06 21:01:42 +02:00
743c76c131 Valuation open matché 2026-06-06 20:41:39 +02:00
acf0d4b687 Plan to transport 2026-06-06 19:57:32 +02:00
d659f14e12 Plan to transport 2026-06-06 19:46:51 +02:00
b74389f9a6 Pnl cron 2026-06-06 11:15:42 +02:00
b3c33ed314 Valuation cron 2026-06-06 11:03:53 +02:00
73006db98a Pnl 2026-06-05 19:51:30 +02:00
3599570d5e Valuation 2026-06-05 19:38:18 +02:00
2ae512e074 Valuation 2026-06-05 19:29:19 +02:00
90932a290d Pnl badge 2026-06-05 19:07:52 +02:00
77922997eb Pnl 2026-06-05 15:25:33 +02:00
574c1a67f4 Mtm 2026-06-05 07:46:48 +02:00
1de1d15064 Pnl 2026-06-04 21:56:25 +02:00
25800aa8a4 Pnl 2026-06-04 21:46:02 +02:00
5a377fda13 pnl 2026-06-04 21:36:25 +02:00
3ab58e63f8 Operator 2026-06-04 19:39:36 +02:00
a114c14e89 Pnl shipment 2026-06-04 19:27:50 +02:00
a98415fe62 Purchase tree 2026-06-04 18:45:09 +02:00
c272630a8a Invoice Melya template 2026-06-04 14:21:40 +02:00
9cc0f0715b Qt type 2026-06-04 12:09:41 +02:00
6860ba9e60 BL date 2026-06-04 10:44:40 +02:00
e9bd6ff57f Demurrage 2026-06-04 09:41:08 +02:00
0efa60f3b5 Demurrage 2026-06-03 20:16:13 +02:00
d3a118db07 Demurrage 2026-06-03 18:35:01 +02:00
48d0b6a3f6 Demurrage 2026-06-03 17:43:16 +02:00
4e1d6895f8 Demurrage 2026-06-03 17:00:51 +02:00
4a0461df44 demurrage 2026-06-03 16:29:50 +02:00
adcce718fd Demurrage 2026-06-03 14:30:16 +02:00
5763ca64a5 Demurrage 2026-06-03 14:14:51 +02:00
0094bd6974 Demurrage 2026-06-03 13:50:28 +02:00
c096b39259 Demurrage 2026-06-03 11:18:27 +02:00
654a8418bf Demurrage 2026-06-03 11:08:32 +02:00
dd4b8055a7 Merge branch 'main' of https://gitea.open-squared.tech/admin/tradon 2026-06-03 10:13:46 +02:00
9556f870ec Demurrage 2026-06-03 10:13:43 +02:00
AzureAD\SylvainDUVERNAY
4c08b95b3b Import Price Bug Fix 2026-06-03 10:05:36 +02:00
AzureAD\SylvainDUVERNAY
a6e90f6e8b Import Price Bug Fix 2026-06-03 09:41:52 +02:00
7df570c860 Demurrage 2026-06-03 09:20:44 +02:00
31d0e00f89 Demurrage 2026-06-03 08:43:05 +02:00
9a1673c6cd Demurrage 2026-06-03 08:37:21 +02:00
fb17c13a61 Demurrage 2026-06-03 08:28:32 +02:00
671251d23e Demurrage 2026-06-03 08:17:49 +02:00
4e7911b0ac Badge 2026-06-02 20:10:11 +02:00
c233e8cb77 Badge 2026-06-02 20:06:34 +02:00
de0ef9fdf4 Badge 2026-06-02 19:58:55 +02:00
c95b273291 Badge 2026-06-02 19:38:14 +02:00
542246baac Badge 2026-06-02 18:10:05 +02:00
0415ced262 Badge 2026-06-02 17:45:02 +02:00
b767f7648d Badge 2026-06-02 17:30:43 +02:00
405c2c2bf0 Merge dev 2026-06-02 14:59:23 +02:00
f39675543a Merge dev 2026-06-02 14:53:07 +02:00
2fecf60295 Notes 2026-06-02 12:00:51 +02:00
3b9a6ca943 Bug premium sale 2026-06-01 14:50:55 +02:00
fcd24af41b Clauses 2026-06-01 00:14:30 +02:00
b0d69207e4 Sale
Clauses
2026-05-31 23:59:55 +02:00
91bf2a3de4 Contract clauses 2026-05-31 23:39:07 +02:00
e9a8355587 Contract clauses 2026-05-31 23:33:25 +02:00
d483e7cf69 Charter party 2026-05-31 22:58:42 +02:00
2dd8360d62 Charter party 2026-05-31 22:33:55 +02:00
779e1aa361 Charter party 2026-05-31 22:07:57 +02:00
2223927d34 Charter party 2026-05-31 21:52:44 +02:00
a26d0516f0 Charter party 2026-05-31 21:47:25 +02:00
5605b19164 Charter party 2026-05-31 21:38:41 +02:00
f8d7f1e9d9 Charter party 2026-05-31 21:34:24 +02:00
735c5431af Premium composition 2026-05-31 20:50:23 +02:00
38c614af98 Pricing Fixed Component 2026-05-31 16:19:23 +02:00
fcd809a3e3 Add Fixed type component 2026-05-31 15:56:18 +02:00
83f7e43a6e Bug fee cog 2026-05-29 08:07:09 +02:00
a9b048d4cf Auto-calculation: formulas recalculate on cell change
- Template: calcMode=auto, calcOnSave=1
- VBA: removed xlCalculationManual, added ForceRefresh macro
  (clears cache + Application.Calculate for manual DB refresh)
- Workflow: change Setup!B6:B9 -> formulas recalculate automatically

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 07:36:06 +02:00
7da99b3bf7 Live UDF formulas in template, remove gApiEnabled guard
- GL_Accounts_Client_Template.xlsx: C6:C13 now have real formulas
  =GetAccountAmount(Setup!$B$6,...) instead of text placeholders
- GLAccountsExcelApi.bas: removed gApiEnabled flag entirely, UDFs
  call RunQuery directly; add SetManualCalculation helper macro
- Works in Excel safe mode + manual calc: F9 triggers DB queries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 01:25:15 +02:00
242406588c Excel desktop POC: direct SQL via VBA, safe mode launcher
- GLAccountsExcelApi.bas: RefreshGLApi reads Setup!B6:B9 directly,
  calls PostgreSQL via ADODB without sheet.Evaluate or live UDF formulas
- Connection string pointed to vps107.geneva.hosting / postgres
- GL_Accounts_Client_Template.xlsx: fixed formula text cell refs (B6 not B7)
- Ouvrir_Excel_SafeMode.bat: launches Excel /safe with template to bypass
  print spooler freeze (splwow64 deadlock on edit mode entry)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 01:13:04 +02:00
3c4261ccf6 ICT bug 2026-05-28 20:18:24 +02:00
2428978d90 ICT bug padding + template 2026-05-28 20:12:39 +02:00
c9028f1590 Add maturity date 2026-05-28 13:59:36 +02:00
1af7ae99a4 Bank Template 2026-05-28 13:46:56 +02:00
26f43c2438 Template Melya 2026-05-28 13:34:56 +02:00
5b5685ae12 Template Melya 2026-05-28 13:10:34 +02:00
28c4fb3ff8 Bug Add Physical 2026-05-27 22:52:02 +02:00
181c4b3d34 Template Melya 2026-05-27 22:03:51 +02:00
50ac9f297c Template Melya 2026-05-27 21:20:20 +02:00
c2d4e34516 Mtm bug 2026-05-27 10:54:30 +02:00
5b5f1d77ff Matching bug 2026-05-27 09:45:16 +02:00
0fffb341fe Go to matching 2026-05-26 18:54:07 +02:00
8b4daeac84 SHipment cancel 2026-05-25 15:55:42 +02:00
4da56e4b8a Fee shipment bug 2026-05-25 14:49:27 +02:00
e7a1a83c90 Fee rules 2026-05-25 14:34:19 +02:00
c6d7b0da2b Fee rules 2026-05-25 14:17:08 +02:00
12727e53cb Fee rules 2026-05-25 13:51:55 +02:00
436d0b9a0c Fee rules 2026-05-25 13:36:11 +02:00
7a39754a63 Matching 2026-05-25 11:15:50 +02:00
55f342f106 Tolérance option 2026-05-25 11:01:45 +02:00
e78977dfa5 Pricing bug 2026-05-25 09:56:43 +02:00
ddca542bfc Pricing matrix 2026-05-25 09:04:24 +02:00
1e5611f726 Pricing 2026-05-25 08:18:59 +02:00
aed68ba6f4 GR 2026-05-24 19:43:54 +02:00
54309a1980 GR 2026-05-24 19:32:03 +02:00
1d398ab4b5 GR 2026-05-24 19:17:43 +02:00
2a5eec0e62 GR 2026-05-24 19:13:58 +02:00
20f51de50f GR 2026-05-24 19:04:58 +02:00
a22d185fb0 GR 2026-05-24 18:50:30 +02:00
390207a371 GR 2026-05-24 18:47:19 +02:00
01c23c1ec2 GR 2026-05-24 18:35:59 +02:00
c7146dd8a7 GR 2026-05-24 18:30:16 +02:00
7569d06254 GR 2026-05-24 18:26:33 +02:00
f0ed5b9bd3 GR 2026-05-24 18:19:57 +02:00
1075f49bd1 GR 2026-05-24 18:13:34 +02:00
1efaad8c38 GR 2026-05-24 18:05:31 +02:00
1dc8803287 GR 2026-05-24 17:53:33 +02:00
50636c1294 GR 2026-05-24 17:38:44 +02:00
6dfb12c125 Global reporting 2026-05-24 17:25:46 +02:00
96653be311 GL detail report 2026-05-21 20:34:01 +02:00
488fb15498 Import prices 2026-05-21 19:29:01 +02:00
f56f394ec3 Import price 2026-05-21 19:22:39 +02:00
4b9621a5f0 Notes 2026-05-21 14:20:51 +02:00
900d733fcd GL detail report 2026-05-21 14:06:16 +02:00
1adc4248ee GL detail report 2026-05-21 13:56:49 +02:00
3c5c20276d GL detail report 2026-05-21 13:47:51 +02:00
f16b334089 GL detail report 2026-05-21 13:39:21 +02:00
f29664fd77 Product origin Melya 2026-05-21 13:02:37 +02:00
f7e70e0438 COO 2026-05-20 16:43:02 +02:00
2cf1b25dad COO adj 2026-05-20 16:15:59 +02:00
27e081228f Price composition 2026-05-20 14:40:25 +02:00
ea3cb65d7d Price composition 2026-05-20 14:29:31 +02:00
9ad0217a69 Price composition 2026-05-20 14:17:36 +02:00
1f3616858a CreateContract 2026-05-20 14:04:22 +02:00
072960307a CreateContract 2026-05-20 14:00:32 +02:00
83612eeea3 Crop bug 2026-05-20 13:36:07 +02:00
8e16259ab2 Deal nb 2026-05-20 11:15:39 +02:00
3a1b9f2d90 Invoice template 2026-05-20 09:52:27 +02:00
22a083a853 Delta neg 2026-05-20 09:15:17 +02:00
c3d768d5a8 Create contract detail 2026-05-20 08:45:27 +02:00
133ca49eda GL detail 2026-05-19 21:34:23 +02:00
f8aded48af GL detail 2026-05-19 20:25:44 +02:00
c6a2d48e4c GL detail 2026-05-19 20:11:34 +02:00
22861dbc91 Targeted Qt 2026-05-19 11:12:52 +02:00
8ff380e229 GL report 2026-05-18 20:23:25 +02:00
80b2fed34b Bug terms 2026-05-17 20:33:20 +02:00
74e6cd0bf2 Targeted Qt 2026-05-17 20:22:08 +02:00
70f4bc291c Jauge 2026-05-17 19:30:55 +02:00
a231f32646 Targeted Qt 2026-05-17 19:22:30 +02:00
d1c0f4a738 Targeted Qt 2026-05-17 19:15:41 +02:00
ce883fa950 Targeted Qt 2026-05-17 19:03:51 +02:00
149306ff4e Targeted Qt 2026-05-17 18:56:23 +02:00
23fbcfa988 Docs 2026-05-17 18:43:09 +02:00
3a31cc354a Jauges bug 2026-05-17 18:28:39 +02:00
62941a5c3b Jauges Go to matching 2026-05-17 18:21:26 +02:00
d2ae65af82 Jauge tolerance 2026-05-17 18:00:36 +02:00
b35fa829e2 Jauge tolerance 2026-05-17 17:22:40 +02:00
9d436d6ea0 Matching 2026-05-17 15:45:54 +02:00
9a6bb83c19 Matching 2026-05-17 15:27:55 +02:00
ba79f88b43 Lots Management header 2026-05-17 11:33:21 +02:00
4d2f203469 Lots Management 2026-05-17 11:13:25 +02:00
c907a87b09 Go to matching action 2026-05-17 10:24:17 +02:00
ff75f9ab33 Go to matching action 2026-05-17 09:58:37 +02:00
3877ff0b6f Go to matching action 2026-05-17 09:50:22 +02:00
74b6425e93 Fees gap 2026-05-16 08:46:23 +02:00
b20c3d54a4 Bug check fee sql 2026-05-15 11:26:39 +02:00
60f26ddc9d Bug check qt 2026-05-14 21:52:10 +02:00
07ddb212e3 Check tolerance 2026-05-14 21:37:49 +02:00
4395884754 Fee check & rules 2026-05-14 19:32:17 +02:00
160ec1544b Bug Create contract 2026-05-14 17:11:54 +02:00
18c0329a94 Bug create contract 2026-05-14 16:59:09 +02:00
cdfc117220 Bug Create contract 2026-05-14 16:54:32 +02:00
a613f59a2d Bug create contract 2026-05-14 16:48:32 +02:00
4925cc8bfd Bug create contract 2026-05-14 16:40:23 +02:00
dd2b6952bc Bug create contract 2026-05-14 16:28:24 +02:00
30b9da1a78 Tolérance 2026-05-14 16:06:12 +02:00
1a041fdcbf Check qt 2026-05-14 15:42:59 +02:00
5f159b06dd Bug Add physical lot 2026-05-14 14:52:10 +02:00
30e0aa6543 Bug Add physical lot 2026-05-14 14:47:02 +02:00
20a4e20e91 Bug pnl fee 2026-05-14 14:31:23 +02:00
82a30f246d Bug check qt 2026-05-14 13:33:14 +02:00
e683ce7e62 Bug check qt 2026-05-14 12:32:31 +02:00
3c788d4764 bug check qt 2026-05-14 12:24:16 +02:00
b4d944b10c bug check qt 2026-05-14 12:18:31 +02:00
24826bec50 bug check qt 2026-05-14 11:52:53 +02:00
9677d0dae2 Bug quantity/th qt 2026-05-14 11:34:47 +02:00
2b3c823743 docs 2026-05-14 11:20:33 +02:00
355831c76d docs 2026-05-14 10:55:47 +02:00
e6bbf2f1c1 docs 2026-05-14 10:40:35 +02:00
0d034816f7 docs 2026-05-14 10:25:39 +02:00
b0a0d31528 notes 2026-05-13 20:46:58 +02:00
a85ab97d46 check qt 2026-05-13 20:28:40 +02:00
14ba0361d2 Check lot quantities 2026-05-13 19:29:24 +02:00
26c955ef53 check code + db 2026-05-13 18:39:54 +02:00
a71ce107f0 lot quantity logic 2026-05-13 17:39:13 +02:00
343fc8527c Add lot_hist readonly 2026-05-13 15:35:23 +02:00
de78da846d Business rules 2026-05-13 15:13:23 +02:00
50e25138fd business rules 2026-05-13 09:56:24 +02:00
f2c7adc2b3 index in docs 2026-05-11 21:14:16 +02:00
c017aac9a7 check qt create contract & matching 2026-05-11 20:42:26 +02:00
09c78395f1 Bug accrual fee + invoice sale side 2026-05-11 20:19:44 +02:00
4ae64269f4 notes 2026-05-10 22:05:33 +02:00
90593b70c2 Lost Management Mark as finished 2026-05-10 21:56:47 +02:00
4d5bbc5523 Hide field ITSA 2026-05-10 19:30:28 +02:00
a23aac4527 Fee currency 2026-05-10 18:45:02 +02:00
e37970a43d Invoice template 2026-05-10 16:13:02 +02:00
fe3a3ea71e Create contract 2026-05-10 16:00:38 +02:00
8e890f3c43 Notes 2026-05-09 18:11:55 +02:00
a684c7b9c1 Matching 2026-05-09 18:06:47 +02:00
28d3400651 Lots Management 2026-05-09 17:46:48 +02:00
000cf54fbc Lots Management 2026-05-09 17:23:54 +02:00
65fe45bae2 Lots Management 2026-05-09 16:27:29 +02:00
d8ac25dcda Lots Management Enhancement 2026-05-09 11:03:51 +02:00
c27c915700 notes 2026-05-06 15:15:52 +02:00
d4da45344c Remove lots 2026-05-06 09:40:53 +02:00
bfa874da43 Accounting notes 2026-05-05 20:14:42 +02:00
199caa2632 add country to sla cost 2026-05-01 16:32:37 +02:00
0f218374a7 Fee adj when th qt change 2026-05-01 15:12:22 +02:00
e03dee7def Th qt modification 2026-05-01 09:16:07 +02:00
83aa474073 rules 2026-04-30 15:38:27 +02:00
75bd34b673 Bug fee financing interest 2026-04-30 13:51:54 +02:00
03d65c253c Bug Pnl lot physic 2026-04-30 13:16:45 +02:00
adac3ca630 Rules 2026-04-29 21:54:36 +02:00
221e701507 Pnl sale side 2026-04-29 20:04:23 +02:00
5a7b5845b6 Pnl sale side open 2026-04-29 20:00:42 +02:00
616cf6d73c fee mode rate sale side 2026-04-29 19:47:59 +02:00
589ff16401 From To filter domain 2026-04-29 19:20:01 +02:00
a7d360b584 template coo 2026-04-29 18:54:23 +02:00
56b173d225 Valuation/ark as finished 2026-04-29 10:13:53 +02:00
27450d51f6 From To 2026-04-28 21:12:25 +02:00
ea31706ff8 From To & Price component 2026-04-28 21:06:15 +02:00
35fe191939 From To 2026-04-28 20:42:37 +02:00
062b42f83f Bug From To 2026-04-28 20:32:04 +02:00
debe374f1b traces 2026-04-28 20:16:38 +02:00
b5b554bad0 From TO line/sale line 2026-04-28 20:07:55 +02:00
1d6f3158c0 Price component filter domain & from to line/sale line 2026-04-28 19:59:33 +02:00
ff2b897b7a From to + pricing component 2026-04-28 19:48:29 +02:00
30d4362c09 Check From To + pricing component domain filter 2026-04-28 19:23:42 +02:00
aa9a18fb93 Bug qt doublée sale 2026-04-28 13:33:37 +02:00
4faa7fd83c bug padding 2026-04-28 12:19:06 +02:00
ca20d418cc Bug 2026-04-28 12:08:09 +02:00
3314d31fcd Bug 2026-04-28 11:57:28 +02:00
f6db19d1d9 Bug 2026-04-28 11:51:07 +02:00
257916bd6c Bug 2026-04-28 11:35:54 +02:00
2cdcbe7303 Bug 2026-04-28 11:27:35 +02:00
98893647f6 Bug 2026-04-28 11:13:30 +02:00
2e276e2b1f Move line manual 2026-04-28 11:09:37 +02:00
384c1285b1 Padding report 2026-04-27 20:09:36 +02:00
308252215f padding doc 2026-04-26 19:19:41 +02:00
7965b22954 padding acc 2026-04-26 15:28:35 +02:00
8ae2c7ca9c padding acc 2026-04-26 15:11:28 +02:00
16151381cc padding acc 2026-04-26 14:59:13 +02:00
4db0395a11 padding acc 2026-04-26 14:56:51 +02:00
fbfa73110f padding acc 2026-04-26 14:35:07 +02:00
9544418897 padding 2026-04-26 11:38:38 +02:00
e40a32e796 padding 2026-04-26 11:30:59 +02:00
530f2f9d97 padding 2026-04-26 10:51:06 +02:00
9879926f08 OS template account 2026-04-23 21:43:03 +02:00
04e9d7dc03 Fee FI bug 2026-04-23 14:22:24 +02:00
7a123b1af6 bug PL qt double * fee rate 2026-04-23 14:07:42 +02:00
a2b053c565 COO 2026-04-23 12:44:33 +02:00
6018429369 COO 2026-04-23 12:34:15 +02:00
4d4e7ab625 COO 2026-04-23 12:29:43 +02:00
68ce1f3a62 proforma adj 2026-04-23 11:10:05 +02:00
d8a56637c9 Bank accounts 2026-04-23 10:59:44 +02:00
a1486175f2 proforma template 2026-04-23 10:46:36 +02:00
228ee07a9c Add Our bank account 2026-04-23 10:15:20 +02:00
036a2801e5 COO report 2026-04-22 21:39:03 +02:00
c2838dcfd3 COO report 2026-04-22 21:26:55 +02:00
6e26f178ce coo report 2026-04-22 21:22:10 +02:00
4458871423 add COO 2026-04-22 21:09:54 +02:00
99782b5b0e Add packing list in account invoice 2026-04-22 20:36:14 +02:00
d09d183cdc Report name 2026-04-22 20:06:48 +02:00
ec45440400 Report name 2026-04-22 19:57:43 +02:00
edb23b008c patch lot sur shipment 2026-04-21 22:06:09 +02:00
fca279a038 bug DI 2026-04-21 21:21:29 +02:00
512862113a bug automation 2026-04-21 21:05:32 +02:00
1ed79a2363 BL nb 2026-04-21 19:39:49 +02:00
05a75f000a DI 2026-04-21 19:24:37 +02:00
77a5366622 DI 2026-04-21 19:19:01 +02:00
c203e1b300 DI 2026-04-21 19:12:38 +02:00
22ad8547e4 DI 2026-04-21 19:01:35 +02:00
2ea2165653 DI 2026-04-21 18:53:44 +02:00
5e72d4f98d document_incoming result_notes 2026-04-21 18:49:47 +02:00
b1680cc96c Add date + result_notes in document_incoming 2026-04-21 18:40:51 +02:00
cfb719a7da cron faircot 2026-04-21 14:59:31 +02:00
9e4a12605c cron faircot 2026-04-21 14:51:26 +02:00
4013f9d74c Correction cron faircot 2026-04-21 14:33:12 +02:00
280cff5fdb Pnl exclude Mark to finished & price on change 2026-04-21 13:22:41 +02:00
897c6f6824 Add constraint delete matched contracts 2026-04-20 10:29:33 +02:00
8906f00d36 Notes 2026-04-10 14:40:06 +02:00
29a719c117 template 2026-04-10 09:15:38 +02:00
d71257720e invoice seq nb 2026-04-10 08:40:53 +02:00
aee9c3277e Add bank to template 2026-04-10 08:24:59 +02:00
b68f475e22 Pricing manual 2026-04-10 07:52:59 +02:00
90eab73430 price component 2026-04-09 22:46:32 +02:00
472806ef06 pricing manuel 2026-04-09 22:38:48 +02:00
0def187750 Add manual pricing 2026-04-09 22:24:49 +02:00
229b6037fb unit correction 2026-04-09 21:55:40 +02:00
8a90216357 net gross 2026-04-09 21:46:28 +02:00
4bbd7a5e76 bug template 2026-04-09 21:38:26 +02:00
9c8d7f11ae kg lbs 2026-04-09 21:23:27 +02:00
b39607d987 del period 2026-04-09 20:58:47 +02:00
65482b4a8b bug Th qt 2026-04-09 20:31:24 +02:00
8b9787d4c0 bug th qt 2026-04-09 20:23:09 +02:00
a1ab7dec82 ICT bulk 2026-04-09 19:46:08 +02:00
5ae8af84fb Add note for bot 2026-04-08 17:15:11 +02:00
c90b14fcc1 Modification on Insurance template 2026-04-08 15:38:26 +02:00
e9ff9c76ab Add Bill template 2026-04-08 11:24:23 +02:00
63d8266a9c Add Bill template 2026-04-08 10:26:25 +02:00
add4cdc137 Add Packing list template 2026-04-08 10:08:17 +02:00
4d94aa78ed Merge branch 'main' of https://gitea.open-squared.tech/admin/tradon 2026-04-08 09:46:05 +02:00
da01249f66 Add Packing list template 2026-04-08 09:46:01 +02:00
AzureAD\SylvainDUVERNAY
5cbb57c657 VSP and SSH configuration 2026-04-07 15:35:06 +02:00
50a8c6328f Doc deploiement 2026-04-07 15:03:37 +02:00
eaa5c8b544 Add Payment order template 2026-04-07 14:15:10 +02:00
78e9e06a8b Add Payment order template 2026-04-07 13:59:16 +02:00
9f06398b2c Th qt correction 2026-04-07 13:42:17 +02:00
51a84f1f2e Add Payment order 2026-04-07 11:54:18 +02:00
00330008d1 Add Purchase order template 2026-04-07 11:42:09 +02:00
3480eb8a7a Add surveyor on shipment 2026-04-07 11:09:52 +02:00
5179d98289 Add insured amount 2026-04-07 10:54:14 +02:00
2109d7a3e4 Add insured amount 2026-04-07 10:32:29 +02:00
1f350e6207 Add session notes 2026-04-06 18:52:15 +02:00
7722292482 Add Insurance template 2026-04-06 18:45:05 +02:00
ec359f6b8a Add insurance template 2026-04-06 17:30:50 +02:00
845b9cf749 Add bank bloc 2026-04-06 16:45:53 +02:00
48b941b109 Add bank in template 2026-04-06 16:29:44 +02:00
18ece66cdb Add bank bloc on template 2026-04-06 16:18:07 +02:00
acfc2fe88a Add invoice_melya.fodt 2026-04-06 15:56:00 +02:00
888b880bd6 Add template management 2026-04-06 15:17:17 +02:00
1f62ae91dd Add multi client template management 2026-04-06 14:56:37 +02:00
05e68636ad Add WR draft 2026-04-06 11:17:24 +02:00
bfb9bb3188 Add WR draft management 2026-04-06 11:02:13 +02:00
b78e64f9f1 Add counter 2026-04-06 09:50:15 +02:00
199b8aec12 Add counter to controller 2026-04-06 09:03:10 +02:00
1757075f2b 03.04.26 2026-04-03 07:39:52 +02:00
172d38479d 02.04.26 2026-04-02 17:28:44 +02:00
4902368b15 02.04.26 2026-04-02 17:20:36 +02:00
7b4f757cb5 02.04.26 2026-04-02 17:12:53 +02:00
b37f132cdf 02.04.26 2026-04-02 17:07:58 +02:00
15f791bd92 02.04.26 2026-04-02 16:46:50 +02:00
58cd66e543 02.04.26 2026-04-02 16:31:05 +02:00
cc6ce82ec1 02.04.26 2026-04-02 16:24:18 +02:00
11526ef3ee 02.04.26 2026-04-02 13:16:17 +02:00
6d52317804 02.04.26 2026-04-02 13:09:04 +02:00
a99efcfc5b 02.04.26 2026-04-02 13:00:19 +02:00
0d5cf7dffc 02.04.26 2026-04-02 12:46:42 +02:00
613b679908 02.04.26 2026-04-02 12:32:06 +02:00
51ced23ab8 02.04.26 2026-04-02 12:12:49 +02:00
2958e1fb9e 02.04.26 2026-04-02 11:33:49 +02:00
346a34951d 02.04.26 2026-04-02 11:16:26 +02:00
b644aea007 02.04.26 2026-04-02 11:03:58 +02:00
5dbaba5f32 02.04.26 2026-04-02 11:00:32 +02:00
d133665fc7 02.04.26 2026-04-02 10:38:00 +02:00
c2cb2a874c 02.04.26 2026-04-02 10:32:36 +02:00
408970c339 01.04.26 2026-04-01 21:48:31 +02:00
ea2627c9ae 01.04.26 2026-04-01 21:40:32 +02:00
ac988a714a 01.04.26 2026-04-01 21:26:46 +02:00
97eae6e4a6 01.04.26 2026-04-01 21:19:31 +02:00
3976b387d7 01.04.26 2026-04-01 18:29:44 +02:00
9b8e8127a1 01.04.26 2026-04-01 18:02:09 +02:00
f53a9bce27 01.04.26 2026-04-01 15:22:21 +02:00
a7753b974f 01.04.26 2026-04-01 14:30:04 +02:00
c687828ba5 01.04.26 2026-04-01 14:09:42 +02:00
5054b64cd0 01.04.26 2026-04-01 11:08:02 +02:00
06922973b7 01.04.26 2026-04-01 10:03:19 +02:00
18ebf7f06c 01.04.26 2026-04-01 09:53:40 +02:00
44c4560f24 01.04.26 2026-04-01 09:47:03 +02:00
7643bf21fb 31.03.26 2026-03-31 17:54:23 +02:00
97677025d7 31.03.26 2026-03-31 17:31:23 +02:00
02fe5b3e5d 31.03.26 2026-03-31 17:28:13 +02:00
6e529deca0 31.03.26 2026-03-31 17:20:49 +02:00
efee365fc6 31.03.26 2026-03-31 17:17:12 +02:00
6bf245ac64 Merge pull request 'dev' (#8) from dev into main
Reviewed-on: #8
2026-03-31 09:57:16 +00:00
238869989a 30.03.26 2026-03-30 19:33:34 +02:00
AzureAD\SylvainDUVERNAY
2fa541e962 Remove groups 2026-03-29 18:41:12 +02:00
AzureAD\SylvainDUVERNAY
ad2f7e6f78 Commit IFRS adjustments 2026-03-29 18:34:54 +02:00
43c62607a8 29.03.26 2026-03-29 18:13:56 +02:00
5cff728d79 29.03.26 2026-03-29 18:05:00 +02:00
AzureAD\SylvainDUVERNAY
d6382f624b Commit 2 2026-03-29 18:02:31 +02:00
AzureAD\SylvainDUVERNAY
806e374ceb Commit 2026-03-29 17:48:47 +02:00
08febb904f 29.03.26 2026-03-29 17:34:42 +02:00
20d733e787 Merge pull request 'main' (#7) from main into dev
Reviewed-on: #7
2026-03-29 13:03:24 +00:00
984b2ba56f 27.03.26 2026-03-27 14:08:07 +01:00
f67e5d8ccc 27.03.26 2026-03-27 08:12:03 +01:00
2bf02e687c 27.03.26 2026-03-27 07:56:03 +01:00
3c45ebd50f 27.03.26 2026-03-27 07:40:40 +01:00
22d186f0ac 27.03.26 2026-03-27 07:30:15 +01:00
0979021f41 27.03.26 2026-03-27 06:51:43 +01:00
af4ae99dc0 26.03.26 2026-03-26 22:08:56 +01:00
11c489f79d 26.03.26 2026-03-26 21:57:02 +01:00
620d6bb604 26.03.26 2026-03-26 21:50:47 +01:00
97cfd13da2 26.03.26 2026-03-26 21:43:25 +01:00
126455bf0f 26.03.26 2026-03-26 21:01:14 +01:00
97cc447e0a 26.03.26 2026-03-26 20:54:08 +01:00
e2bbd0e522 26.03.26 2026-03-26 20:31:45 +01:00
51778bda9e 26.03.26 2026-03-26 20:09:33 +01:00
c06ea3bd99 26.03.26 2026-03-26 19:58:14 +01:00
f0b0666773 26.03.26 2026-03-26 19:50:27 +01:00
edca5fed55 26.03.26 2026-03-26 19:30:48 +01:00
8b6b93171f 26.03.26 2026-03-26 19:15:14 +01:00
841f7a1c20 26.03.26 2026-03-26 19:02:01 +01:00
91acaba3dc 26.03.26 2026-03-26 18:28:43 +01:00
3e5320cf9e 26.03.26 2026-03-26 15:43:45 +01:00
f9010ddefd 26.03.26 2026-03-26 15:23:42 +01:00
d722b58f4e 26.03.26 2026-03-26 15:20:36 +01:00
3e646ea035 26.03.26 2026-03-26 15:16:34 +01:00
90f97daa7f 26.03.26 2026-03-26 11:13:35 +01:00
e1488e9677 26.03.26 2026-03-26 09:25:34 +01:00
0bcec5d3c4 25.03.26 2026-03-25 20:32:07 +01:00
10848ed533 25.03.26 2026-03-25 20:17:57 +01:00
b90d65d245 25.03.26 2026-03-25 20:09:51 +01:00
357478e74c 25.03.26 2026-03-25 19:29:32 +01:00
1f49c00cff 25.03.26 2026-03-25 18:45:54 +01:00
d38d0324d5 25.03.26 2026-03-25 16:49:05 +01:00
b8ec7a3d2d 25.03.26 2026-03-25 11:01:26 +01:00
0ac261b670 25.03.26 2026-03-25 09:59:15 +01:00
51411faff2 24.03.26 2026-03-24 11:49:14 +01:00
a94906bb53 23.03.26 2026-03-23 21:16:51 +01:00
2262e79361 23.03.26 2026-03-23 20:10:30 +01:00
5327ca4f21 23.03.26 2026-03-23 18:27:54 +01:00
03997db3e4 23.03.26 2026-03-23 18:03:59 +01:00
74e4ac0c1b 23.03.26 2026-03-23 17:57:26 +01:00
353cf17af7 23.03.26 2026-03-23 17:47:36 +01:00
412f8f00e0 23.03.26 2026-03-23 17:38:04 +01:00
6b841db4ce 23.03.26 2026-03-23 17:09:11 +01:00
4e3818d46a 23.03.26 2026-03-23 16:51:32 +01:00
8bb34aa4fb 23.03.26 2026-03-23 16:44:46 +01:00
0392b191c5 23.03.26 2026-03-23 16:34:28 +01:00
f98b0fd010 23.03.26 2026-03-23 16:25:22 +01:00
97dd672e6a 23.03.26 2026-03-23 16:09:39 +01:00
bde600e86a 23.03.26 2026-03-23 15:55:39 +01:00
0e7cabdc00 23.03.26 2026-03-23 14:13:09 +01:00
15f9f4b724 23.03.26 2026-03-23 14:00:28 +01:00
c24de18fcd 23.03.26 2026-03-23 13:49:32 +01:00
cb9c868e88 23.03.26 2026-03-23 11:26:23 +01:00
0e76aa6312 23.03.26 2026-03-23 11:18:58 +01:00
062ca10340 23.03.26 2026-03-23 11:13:50 +01:00
175c7a5e30 23.03.26 2026-03-23 10:49:36 +01:00
e6519d30ee 23.03.26 2026-03-23 10:43:02 +01:00
f9ee416d99 23.03.26 2026-03-23 10:36:33 +01:00
80deac62f8 23.03.26 2026-03-23 10:16:56 +01:00
b5d771341d 23.03.26 2026-03-23 10:10:53 +01:00
afdbd05375 23.03.26 2026-03-23 09:31:27 +01:00
36b94b98af 23.03.26 2026-03-23 09:10:55 +01:00
c655eb4170 22.03.26 2026-03-22 21:10:26 +01:00
ed11b23f79 22.03.26 2026-03-22 20:53:42 +01:00
140eae06a0 22.03.26 2026-03-22 19:11:12 +01:00
234084f073 22.03.26 2026-03-22 19:06:19 +01:00
2fc6fbbd76 22.03.26 2026-03-22 19:02:53 +01:00
0b9c85f5ad 22.03.26 2026-03-22 18:58:46 +01:00
717b51ad19 22.03.26 2026-03-22 18:51:48 +01:00
dfdaf7a1cd 22.03.26 2026-03-22 17:03:58 +01:00
7dba25c6d7 22.03.26 2026-03-22 15:25:08 +01:00
765d90512d 22.03.26 2026-03-22 15:02:24 +01:00
090b4ea5c6 22.03.26 2026-03-22 14:53:53 +01:00
1326c8df4c 22.03.26 2026-03-22 11:27:48 +01:00
d10d805753 22.03.26 2026-03-22 11:22:09 +01:00
85bb272edf 22.03.26 2026-03-22 09:43:36 +01:00
5f475d5714 22.03.26 2026-03-22 09:17:30 +01:00
8cc091ef57 22.03.26 2026-03-22 09:15:19 +01:00
7ba170e072 21.03.26 2026-03-21 22:18:29 +01:00
931b5b9ea2 21.03.26 2026-03-21 22:11:23 +01:00
eb98ef87bb 21.03.26 2026-03-21 22:03:43 +01:00
834ae46a14 21.03.26 2026-03-21 21:56:40 +01:00
7c746ad931 21.03.26 2026-03-21 20:10:55 +01:00
95f5c4af57 21.03.26 2026-03-21 20:00:42 +01:00
f83c62ffa8 21.03.26 2026-03-21 19:46:50 +01:00
1f4b36633b 21.03.26 2026-03-21 19:39:15 +01:00
36dc48bee0 21.03.26 2026-03-21 19:10:08 +01:00
fc52aa8bbb 21.03.26 2026-03-21 18:48:17 +01:00
5c516465e2 21.03.26 2026-03-21 18:36:48 +01:00
d827fa6140 21.03.26 2026-03-21 18:31:04 +01:00
93d27059af 21.03.26 2026-03-21 18:25:30 +01:00
d2a13b3c01 20.03.26 2026-03-20 14:38:42 +01:00
22c4766e66 20.03.26 2026-03-20 14:36:43 +01:00
73eeee0f72 20.03.26 2026-03-20 14:29:46 +01:00
260300681f 20.03.26 2026-03-20 13:53:10 +01:00
a2e0c36a3c 20.03.26 2026-03-20 13:50:31 +01:00
4f6641ceeb 20.03.26 2026-03-20 13:43:47 +01:00
cabd032bc6 20.03.26 2026-03-20 10:45:34 +01:00
b2e095aad5 20.03.26 2026-03-20 10:36:27 +01:00
8c3ba0a23d 20.03.26 2026-03-20 10:28:09 +01:00
13072de28f 20.03.26 2026-03-20 10:19:50 +01:00
a63a46cc40 20.03.26 2026-03-20 10:05:30 +01:00
bf6e9f6a78 20.03.26 2026-03-20 10:01:22 +01:00
6c7947c7c1 20.03.26 2026-03-20 09:11:57 +01:00
6954044f7e 20.03.26 2026-03-20 09:08:13 +01:00
d6207855a0 18.03.26 2026-03-18 18:27:29 +01:00
a41ec4412d 18.03.26 2026-03-18 18:02:49 +01:00
8c3c224c07 18.03.26 2026-03-18 11:06:05 +01:00
3581b7ea80 18.03.26 2026-03-18 10:53:29 +01:00
f7d87d3a78 17.03.26 2026-03-17 20:55:55 +01:00
5a52f09f97 17.03.26 2026-03-17 20:53:32 +01:00
420577cd72 17.03.26 2026-03-17 20:26:11 +01:00
01777f1536 17.03.26 2026-03-17 20:04:11 +01:00
24c8cd8075 17.03.26 2026-03-17 18:16:29 +01:00
7ace327d3f 17.03.26 2026-03-17 14:36:55 +01:00
a8d37bd766 17.03.26 2026-03-17 14:11:33 +01:00
6b96cdbc88 17.03.26 2026-03-17 13:58:08 +01:00
5bb1618b30 16.03.26 2026-03-16 22:35:09 +01:00
3ef819bf86 16.03.26 2026-03-16 22:26:45 +01:00
7ae54f9977 16.03.26 2026-03-16 22:19:15 +01:00
410604d890 16.03.26 2026-03-16 22:14:30 +01:00
c8bbf9a12e 16.03.26 2026-03-16 22:04:21 +01:00
f32cce6177 16.03.26 2026-03-16 21:33:57 +01:00
32b48cf6d1 16.03.26 2026-03-16 21:01:43 +01:00
eebcf936fe 16.03.26 2026-03-16 20:43:05 +01:00
127a38f857 16.03.26 2026-03-16 20:34:16 +01:00
1cedd11304 16.03.26 2026-03-16 20:28:10 +01:00
8881caccd0 16.03.26 2026-03-16 19:47:43 +01:00
59a6554fa3 16.03.26 2026-03-16 19:35:22 +01:00
3d80a6ba5e 16.03.26 2026-03-16 19:25:56 +01:00
07816b9cfe 16.03.26 2026-03-16 19:19:32 +01:00
5bbd68448a 16.03.26 2026-03-16 18:38:15 +01:00
185f26f31b 16.03.26 2026-03-16 15:40:19 +01:00
bbb88dbd22 16.03.26 2026-03-16 15:26:08 +01:00
3ad2ae2624 16.03.26 2026-03-16 15:24:46 +01:00
e78eb80d08 16.03.26 2026-03-16 14:46:11 +01:00
498da9a728 16.03.26 2026-03-16 14:41:29 +01:00
eac35b849f 16.03.26 2026-03-16 14:21:41 +01:00
13770cc299 16.03.26 2026-03-16 14:15:42 +01:00
72b71675ae 16.03.26 2026-03-16 11:08:47 +01:00
5cf5ca520a 16.03.26 2026-03-16 10:59:28 +01:00
5fe0be5c59 16.03.26 2026-03-16 10:48:54 +01:00
c504b4b2d7 16.03.26 2026-03-16 10:41:50 +01:00
bbd3d30b37 16.03.26 2026-03-16 10:32:27 +01:00
b4d09d3a69 16.03.26 2026-03-16 10:23:13 +01:00
8e7f9648fc 16.03.26 2026-03-16 10:06:58 +01:00
2183d8d5a4 16.03.26 2026-03-16 09:52:15 +01:00
8398a8c212 15.03.26 2026-03-15 15:59:16 +01:00
8859ccad1a 15.03.26 2026-03-15 15:53:38 +01:00
c2adc96fbf 15.03.26 2026-03-15 15:26:56 +01:00
9330662b30 15.03.26 2026-03-15 15:21:23 +01:00
eadbbbbbc8 15.03.26 2026-03-15 15:15:22 +01:00
5b12e5f8db 15.03.26 2026-03-15 13:06:10 +01:00
1d10591cf6 15.03.26 2026-03-15 12:02:50 +01:00
fd3ce2188a 15.03.26 2026-03-15 11:59:15 +01:00
4695a93e44 15.03.26 2026-03-15 11:48:43 +01:00
93f0e6b0af 15.03.26 2026-03-15 11:45:42 +01:00
319d8a4afb 15.03.26 2026-03-15 11:28:29 +01:00
b6e85ee710 15.03.26 2026-03-15 10:58:53 +01:00
c349441874 13.03.26 2026-03-13 14:58:00 +01:00
73fdbe9eba 13.03.26 2026-03-13 14:51:55 +01:00
b20b0c8df6 13.03.26 2026-03-13 14:47:13 +01:00
cc5f4da38c 13.03.26 2026-03-13 14:28:23 +01:00
016112a355 13.03.26 2026-03-13 10:43:58 +01:00
0cf03a75b0 13.03.26 2026-03-13 10:24:54 +01:00
7f6b93094f 12.03.26 2026-03-12 15:20:29 +01:00
ad58d40da7 12.03.26 2026-03-12 15:14:03 +01:00
f0c1b8909e 12.03.26 2026-03-12 14:56:09 +01:00
8e8afe39d0 12.03.26 2026-03-12 14:44:04 +01:00
8da50c72c7 12.03.26 2026-03-12 11:39:43 +01:00
683d3600ac 12.03.26 2026-03-12 10:56:52 +01:00
e2cb840844 12.03.26 2026-03-12 10:51:39 +01:00
19a4363d10 12.03.26 2026-03-12 10:46:48 +01:00
23c4edfec5 12.03.26 2026-03-12 10:24:34 +01:00
b07ad57a36 11.03.26 2026-03-11 22:17:48 +01:00
6d06125360 11.03.26 2026-03-11 22:12:38 +01:00
4a14a78f78 11.03.26 2026-03-11 21:33:42 +01:00
2fba795b11 11.03.26 2026-03-11 21:27:43 +01:00
b23dba865f 11.03.26 2026-03-11 21:14:53 +01:00
0b1bb2ffa5 11.03.26 2026-03-11 21:06:45 +01:00
6890d0de07 11.03.26 2026-03-11 20:53:20 +01:00
c180e8926f 11.03.26 2026-03-11 20:51:46 +01:00
0fe16df326 11.03.26 2026-03-11 20:31:47 +01:00
3e15052520 11.03.26 2026-03-11 20:14:47 +01:00
dee1896a6c 10.03.26 2026-03-10 21:18:19 +01:00
faf5fa605e 10.03.26 2026-03-10 15:33:22 +01:00
a7846b359c 10.03.26 2026-03-10 15:21:47 +01:00
c0cffde079 10.03.26 2026-03-10 15:01:09 +01:00
814071e4c1 10.03.26 2026-03-10 14:49:10 +01:00
30ae86a987 10.03.26 2026-03-10 14:44:16 +01:00
1de11a846b 10.03.26 2026-03-10 14:29:12 +01:00
93acacb955 10.03.26 2026-03-10 13:04:13 +01:00
6d053dfe03 10.03.26 2026-03-10 12:59:07 +01:00
5f49e01495 10.03.26 2026-03-10 12:54:46 +01:00
d609864822 10.03.26 2026-03-10 12:46:03 +01:00
aff4459942 09.03.26 2026-03-09 20:35:42 +01:00
840f03e5d8 09.03.26 2026-03-09 20:32:51 +01:00
039b278757 09.03.26 2026-03-09 20:27:08 +01:00
265b41b206 09.03.26 2026-03-09 20:15:09 +01:00
5235b381c6 09.03.26 2026-03-09 20:10:53 +01:00
52c58df547 09.03.26 2026-03-09 20:01:20 +01:00
cc8540ee91 09.03.26 2026-03-09 18:17:32 +01:00
d70a784db8 09.03.26 2026-03-09 18:05:08 +01:00
e6984e3393 09.03.26 2026-03-09 17:59:26 +01:00
f4ae7ebd7c 09.03.26 2026-03-09 17:56:21 +01:00
5f744ec25c 09.03.26 2026-03-09 17:53:54 +01:00
1dc69008d8 09.03.26 2026-03-09 17:50:26 +01:00
96aab15a7a 09.03.26 2026-03-09 16:52:19 +01:00
dd06fc523c 09.03.26 2026-03-09 16:11:42 +01:00
7cafd8381f 09.03.26 2026-03-09 15:49:51 +01:00
c009b15ed0 09.03.26 2026-03-09 15:35:27 +01:00
6717feb22d 09.03.26 2026-03-09 15:29:29 +01:00
7998467adf 09.03.26 2026-03-09 15:24:15 +01:00
83ef3c6ae9 09.03.26 2026-03-09 15:17:34 +01:00
372d04b30f 08.03.26 2026-03-08 09:09:10 +01:00
4e6ddc39c1 08.03.26 2026-03-08 08:53:02 +01:00
a3f15c30e6 06.03.26 2026-03-06 14:24:18 +01:00
cdb0bf7254 05.03.26 2026-03-05 19:49:16 +01:00
843ade6f3d 05.03.26 2026-03-05 19:38:03 +01:00
62f731530b 05.03.26 2026-03-05 19:34:46 +01:00
14c2548c16 05.03.26 2026-03-05 19:32:55 +01:00
13000f110d 05.03.26 2026-03-05 19:25:17 +01:00
915fd17d83 05.03.26 2026-03-05 18:38:24 +01:00
12de27d105 05.03.26 2026-03-05 18:16:22 +01:00
1a1f675cf6 05.03.26 2026-03-05 18:12:22 +01:00
3b67f3c251 05.03.26 2026-03-05 18:10:49 +01:00
551a040df0 05.03.26 2026-03-05 18:07:54 +01:00
b6c3279917 05.03.26 2026-03-05 18:01:05 +01:00
2b956e6142 05.03.26 2026-03-05 17:53:49 +01:00
ae0817e0ac 05.03.26 2026-03-05 15:30:11 +01:00
7451e19125 05.03.26 2026-03-05 12:30:47 +01:00
52418b6244 05.03.26 2026-03-05 12:27:01 +01:00
a577595ee6 05.03.26 2026-03-05 11:24:40 +01:00
8db31ddaf0 05.03.26 2026-03-05 11:03:44 +01:00
e371b116f7 05.03.26 2026-03-05 10:58:01 +01:00
3212d551d2 05.03.26 2026-03-05 10:54:10 +01:00
8d557a63a0 05.03.26 2026-03-05 10:50:48 +01:00
ee9866c28e 05.03.26 2026-03-05 10:37:46 +01:00
8d9dd6c275 05.03.26 2026-03-05 10:21:37 +01:00
0841fb4609 05.03.26 2026-03-05 10:13:43 +01:00
5b059b70e6 04.03.26 2026-03-04 18:59:39 +01:00
3319563b22 04.06.26 2026-03-04 18:52:02 +01:00
ddc2884f61 04.03.26 2026-03-04 18:29:56 +01:00
5dfd24b5de 04.03.26 2026-03-04 18:05:42 +01:00
7ae145b6c1 04.03.26 2026-03-04 17:56:58 +01:00
7909b10636 04.03.26 2026-03-04 17:43:53 +01:00
bab6c14499 04.03.26 2026-03-04 17:32:49 +01:00
c816ecdbb7 04.03.26 2026-03-04 17:23:14 +01:00
024f8f78bd 04.03.26 2026-03-04 17:09:32 +01:00
94595346b7 04.03.26 2026-03-04 16:54:32 +01:00
8a4d730c5a 04.03.26 2026-03-04 16:35:29 +01:00
3226f55a7d 04.03.26 2026-03-04 16:34:25 +01:00
8eba77b26c 04.03.26 2026-03-04 16:19:40 +01:00
a0c1408f8e 04.03.26 2026-03-04 16:18:42 +01:00
7e726010b8 03.03.26 2026-03-03 20:10:02 +01:00
4abd9b7e85 03.03.26 2026-03-03 19:49:15 +01:00
e984c026b9 03.03.26 2026-03-03 19:48:09 +01:00
4affffaf0a 03.03.26 2026-03-03 19:34:31 +01:00
c55338e879 03.03.26 2026-03-03 16:30:37 +01:00
1c288d9646 03.03.26 2026-03-03 16:20:38 +01:00
d37bad643d 03.03.26 2026-03-03 16:08:37 +01:00
f493901e8d 03.03.26 2026-03-03 14:00:05 +01:00
934f3511f7 03.03.26 2026-03-03 13:56:57 +01:00
62a1bc69f7 03.03.26 2026-03-03 13:54:04 +01:00
741389b71c 03.03.26 2026-03-03 13:50:58 +01:00
9dd4543fc1 03.03.26 2026-03-03 13:47:46 +01:00
af89743b40 03.03.26 2026-03-03 13:23:19 +01:00
7f0400400c 03.03.26 2026-03-03 12:05:07 +01:00
d9cf6c9a49 03.03.26 2026-03-03 12:00:35 +01:00
1a9114da7e 02.03.26 2026-03-02 10:11:37 +01:00
b5bc559b11 27.02.26 2026-02-27 07:21:16 +01:00
8e67c7aecb 27.02.26 2026-02-27 07:17:24 +01:00
6b884af9cc 27.02.26 2026-02-27 07:04:54 +01:00
e768fe71a3 27.02.26 2026-02-27 06:55:07 +01:00
5f8d081b0e 27.02.26 2026-02-27 06:32:45 +01:00
20c3dacded 27.02.26 2026-02-27 06:24:01 +01:00
6fdf5e1994 27.02.26 2026-02-27 06:17:55 +01:00
92210a381a 27.02.26 2026-02-27 06:12:39 +01:00
cd2a82f61c 27.02.26 2026-02-27 05:56:00 +01:00
4eae444c93 27.02.26 2026-02-27 05:48:07 +01:00
02993336de 26.02.26 2026-02-26 21:32:41 +01:00
2c38d2742b 26.02.26 2026-02-26 20:32:42 +01:00
a724e0a086 26.02.26 2026-02-26 20:21:42 +01:00
399d9da9e5 26.02.26 2026-02-26 16:38:38 +01:00
6f0e0d65ee 26.02.26 2026-02-26 16:28:43 +01:00
20f2b87d99 24.02.26 2026-02-24 20:25:32 +01:00
56f2c3a4a6 24.02.26 2026-02-24 19:48:12 +01:00
a1a13a6846 24.02.26 2026-02-24 19:24:31 +01:00
c244cf658c 23.02.26 2026-02-23 15:33:09 +01:00
6fc0e5982a 19.02.26 2026-02-19 22:53:36 +01:00
84fa03bf77 19.02.26 2026-02-19 22:19:27 +01:00
11cabcee32 18.02.26 2026-02-18 16:58:53 +01:00
1cdc54e59e 18.02.26 2026-02-18 16:37:20 +01:00
d2288e009f 18.02.26 2026-02-18 16:04:35 +01:00
d81b19bfc6 18.02.26 2026-02-18 15:13:50 +01:00
b2fb1e9c4c 18.02.26 2026-02-18 13:32:48 +01:00
cac84abbef 18.02.26 2026-02-18 12:14:35 +01:00
191abf022f 18.02.26 2026-02-18 12:03:40 +01:00
0011a7f943 18.02.26 2026-02-18 11:03:27 +01:00
697b53e68a 16.02.26 2026-02-16 20:01:57 +01:00
0e5c40e9a2 16.02.26 2026-02-16 19:55:09 +01:00
bb313d29bf 1602.26 2026-02-16 15:46:43 +01:00
3f54cbef6a 15.02.26 2026-02-15 15:54:44 +01:00
00cae2f82b 15.02.26 2026-02-15 15:49:40 +01:00
d1d37ee4b3 15.02.26 2026-02-15 15:33:32 +01:00
08a66bc219 15.02.26 2026-02-15 15:20:55 +01:00
35c9731a85 15.02.26 2026-02-15 15:02:42 +01:00
d458e9849d 15.02.26 2026-02-15 14:54:10 +01:00
b94d6e911d 15.02.26 2026-02-15 14:45:31 +01:00
4fb39d564e 15.02.26 2026-02-15 14:37:04 +01:00
10b370f11b 15.02.26 2026-02-15 14:33:14 +01:00
479e0d4d5a 15.02.26 2026-02-15 14:23:33 +01:00
4059cd591e 15.02.26 2026-02-15 12:47:07 +01:00
9565e82850 15.02.26 2026-02-15 11:37:46 +01:00
29ada1899e 15.02.26 2026-02-15 11:27:59 +01:00
67d3ce545e 15.02.26 2026-02-15 10:16:08 +01:00
9b8d372fa6 15.02.26 2026-02-15 10:13:02 +01:00
dda6a63e74 15.02.26 2026-02-15 10:08:31 +01:00
2fc89282b9 15.02.26 2026-02-15 10:02:55 +01:00
cca82dd9f7 14.02.26 2026-02-15 09:40:05 +01:00
1e51061ddc 14.02.26 2026-02-15 09:36:08 +01:00
267e5a3509 14.12.26 2026-02-14 21:30:28 +01:00
e040a4fc11 14.02.26 2026-02-14 20:59:35 +01:00
6f4649c778 14.02.26 2026-02-14 20:19:52 +01:00
177f862263 14.02.26 2026-02-14 20:16:28 +01:00
93456e9530 14.02.26 2026-02-14 20:12:56 +01:00
8ee9354327 14.02.26 2026-02-14 18:46:52 +01:00
6f4ad3723f 14.02.26 2026-02-14 18:41:16 +01:00
f3370e89ea 14.02.26 2026-02-14 18:36:26 +01:00
330fc8d320 14.02.26 2026-02-14 18:28:15 +01:00
f0b979ac8c 14.02.26 2026-02-14 18:16:29 +01:00
f3ebeb7cd3 14.02.26 2026-02-14 18:02:08 +01:00
981a97ed05 14.02.26 2026-02-14 17:53:36 +01:00
9ed4d5f6bb 14.02.26 2026-02-14 17:38:43 +01:00
e43189f052 14.02.26 2026-02-14 16:07:08 +01:00
0fcaca2b5a 14.02.26 2026-02-14 08:55:57 +01:00
3db2f8c96e 14.02.26 2026-02-14 08:52:31 +01:00
b0ebc02434 14.02.26 2026-02-14 08:47:57 +01:00
0be0194ab5 14.02.26 2026-02-14 08:32:38 +01:00
7d99f56fa5 13.02.26 2026-02-13 17:15:48 +01:00
6eec004781 13.02.26 2026-02-13 09:53:54 +01:00
70603d95d4 11.02.26 2026-02-11 13:14:58 +01:00
c78f8b1079 11.02.26 2026-02-11 12:10:29 +01:00
9ec76410f7 11.02.26 2026-02-11 08:41:58 +01:00
e47023c4ba 11.02.26 2026-02-11 08:32:24 +01:00
133805eab5 11.02.26 2026-02-11 08:23:00 +01:00
878588d567 11.02.26 2026-02-11 08:20:23 +01:00
d88a9f204c 11.02.26 2026-02-11 08:13:20 +01:00
1ab2b56f32 11.02.26 2026-02-11 07:58:02 +01:00
b41fe0fa54 11.02.26 2026-02-11 07:43:25 +01:00
1326669f01 11.02.26 2026-02-11 07:36:47 +01:00
e37cd4a1cc 11.02.26 2026-02-11 07:26:48 +01:00
1eb86892de 10.02.26 2026-02-10 15:08:45 +01:00
91095f28e9 09.02.26 2026-02-09 20:55:10 +01:00
e41ad51634 09.02.26 2026-02-09 20:30:56 +01:00
2eabfe2587 09.02.26 2026-02-09 15:13:57 +01:00
a71d666619 09.02.26 2026-02-09 14:50:27 +01:00
38254fee48 09.02.26 2026-02-09 14:49:01 +01:00
5cc66b3a3a 08.02.26 2026-02-08 22:49:57 +01:00
d007db7a1b 08.02.26 2026-02-08 22:46:43 +01:00
935862a66c 08.02.26 2026-02-08 22:31:50 +01:00
0c83fcf35e 08.02.26 2026-02-08 22:26:24 +01:00
bc5aa57319 08.02.26 2026-02-08 22:18:54 +01:00
231b733222 08.02.26 2026-02-08 22:06:19 +01:00
c02d7212bb 08.02.26 2026-02-08 21:54:51 +01:00
df5ef074ad 08.02.26 2026-02-08 21:51:04 +01:00
04179fde77 08.02.26 2026-02-08 21:36:01 +01:00
0cd9d63b15 08.02.26 2026-02-08 21:28:17 +01:00
b5b2f1c362 08.02.26 2026-02-08 21:25:22 +01:00
14dd557e58 08.02.26 2026-02-08 19:24:31 +01:00
1e7cd1c7be 08.02.26 2026-02-08 19:19:57 +01:00
0a04774fac 08.02.26 2026-02-08 19:15:12 +01:00
720ad804a4 08.02.26 2026-02-08 18:10:36 +01:00
175b10e59d 08.02.26 2026-02-08 18:05:29 +01:00
03d923441e 08.02.26 2026-02-08 16:55:56 +01:00
3da61070eb 08.02.26 2026-02-08 15:11:53 +01:00
6a0acb1c20 08.02.26 2026-02-08 10:58:08 +01:00
3195994444 08.02.26 2026-02-08 10:42:18 +01:00
7ab361c2b8 08.02.26 2026-02-08 10:27:59 +01:00
894375c143 08.02.26 2026-02-08 10:15:36 +01:00
225df5c6da 08.02.26 2026-02-08 10:08:37 +01:00
1a2d595303 08.02.26 2026-02-08 09:40:37 +01:00
cccff5726c 08.02.26 2026-02-08 09:37:53 +01:00
9a354181b0 08.02.26 2026-02-08 09:34:35 +01:00
c7dce8bedc 08.02.26 2026-02-08 09:31:31 +01:00
adecdbdb4f 08.02.26 2026-02-08 09:28:30 +01:00
06d40ddbae 08.02.26 2026-02-08 09:25:34 +01:00
e009a9e1e3 08.02.26 2026-02-08 09:22:43 +01:00
d22610abb3 08.02.26 2026-02-08 09:18:35 +01:00
8c033f4eab 08.02.26 2026-02-08 09:14:52 +01:00
cb6c1819f4 08.02.26 2026-02-08 09:09:03 +01:00
762cfc66c1 08.02.26 2026-02-08 09:05:47 +01:00
1d653b2756 08.02.26 2026-02-08 09:04:04 +01:00
b7927b787d 08.02.26 2026-02-08 09:00:54 +01:00
e57019b39c 08.02.26 2026-02-08 08:58:03 +01:00
a09a88ff5f 08.02.26 2026-02-08 08:52:30 +01:00
c1a4b442a3 07.02.26 2026-02-07 20:21:51 +01:00
34166dedc6 07.02.26 2026-02-07 19:31:54 +01:00
9095f3b470 07.02.26 2026-02-07 19:18:42 +01:00
5255959614 07.02.26 2026-02-07 17:11:13 +01:00
1b92b1d207 07.02.26 2026-02-07 17:08:29 +01:00
67eca35a9a 07.02.26 2026-02-07 16:40:48 +01:00
a35e304561 07.02.26 2026-02-07 16:39:34 +01:00
f93c81624d 07.02.26 2026-02-07 16:32:12 +01:00
d47c2cb5fb 07.02.26 2026-02-07 16:28:00 +01:00
45ab8e8904 07.02.26 2026-02-07 16:25:37 +01:00
0f3497e867 07.02.26 2026-02-07 16:23:58 +01:00
b0e19226eb 07.02.26 2026-02-07 16:13:36 +01:00
95589bcf73 07.02.26 2026-02-07 16:10:00 +01:00
03c7f41457 07.02.26 2026-02-07 16:07:54 +01:00
cd0c068b3f 05.02.26 2026-02-05 19:54:14 +01:00
05d5d85bee 05.02.26 2026-02-05 19:46:23 +01:00
d213328fd9 05.02.26 2026-02-05 19:40:38 +01:00
9235c22e04 05.02.26 2026-02-05 19:36:55 +01:00
c1ca8c707c 05.02.26 2026-02-05 19:29:06 +01:00
6c4ded50d0 05.02.26 2026-02-05 19:19:45 +01:00
86b8d3e250 05.02.26 2026-02-05 18:19:09 +01:00
7b94c56e7c 05.02.26 2026-02-05 18:13:10 +01:00
c6f963b6be 05.02.26 2026-02-05 18:04:07 +01:00
181217e755 05.02.26 2026-02-05 17:09:54 +01:00
00a6a5debe 05.02.26 2026-02-05 17:06:00 +01:00
91f85ea7c1 05.02.26 2026-02-05 16:48:16 +01:00
aaf4a264a1 04.02.26 2026-02-04 21:43:53 +01:00
b03a97d02d 04.02.26 2026-02-04 21:34:46 +01:00
a69a9dcb57 04.02.26 2026-02-04 21:20:34 +01:00
0a88b26160 04.02.26 2026-02-04 21:06:10 +01:00
3a79652c59 04.02.26 2026-02-04 18:40:28 +01:00
a0ed097b6c 04.02.26 2026-02-04 18:34:16 +01:00
a9d6f8fa58 04.02.26 2026-02-04 18:18:50 +01:00
2ebad01847 04.02.26 2026-02-04 18:02:09 +01:00
71716c1ad5 04.02.26 2026-02-04 15:40:30 +01:00
240f35af26 04.02.26 2026-02-04 15:18:06 +01:00
1ebbb874ef 04.02.26 2026-02-04 15:00:37 +01:00
eef972c679 03.02.26 2026-02-03 20:26:05 +01:00
011af03e8a 03.02.26 2026-02-03 20:18:55 +01:00
24843ab72d 03.02.26 2026-02-03 19:33:01 +01:00
65c0053df4 03.02.26 2026-02-03 16:01:54 +01:00
f391e17fb6 03.02.26 2026-02-03 15:57:12 +01:00
44fc7dc855 03.02.26 2026-02-03 15:53:32 +01:00
99d895c951 03.02.26 2026-02-03 15:43:38 +01:00
39e59b5b8b 03.02.26 2026-02-03 15:09:24 +01:00
7bc3e350ba 03.02.26 2026-02-03 15:06:27 +01:00
fe7c10d527 03.02.26 2026-02-03 14:33:40 +01:00
272875eb69 03.02.26 2026-02-03 14:30:36 +01:00
16df1c99a6 03.02.26 2026-02-03 14:03:46 +01:00
403db5133e 03.02.26 2026-02-03 13:58:00 +01:00
43312ba412 03.02.26 2026-02-03 13:49:05 +01:00
f4a53f2705 03.02.26 2026-02-03 12:19:44 +01:00
0a41765551 02.02.26 2026-02-02 19:27:23 +01:00
28789200c3 02.02.26 2026-02-02 18:25:39 +01:00
350e41714f 02.02.26 2026-02-02 17:56:51 +01:00
84b70b73c5 02.02.26 2026-02-02 17:51:26 +01:00
68e0000afc 02.02.26 2026-02-02 17:44:47 +01:00
33eeabf5e1 02.02.26 2026-02-02 17:33:40 +01:00
a4f0a08469 02.02.26 2026-02-02 17:27:14 +01:00
7f7b103945 02.02.26 2026-02-02 17:17:49 +01:00
8fddb599ef 02.02.26 2026-02-02 17:10:30 +01:00
313d574a08 02.02.26 2026-02-02 17:05:27 +01:00
f7d6ed00fd 02.02.26 2026-02-02 17:02:14 +01:00
6c570acfe6 02.02.26 2026-02-02 16:49:23 +01:00
2ba836c284 02.02.26 2026-02-02 16:42:57 +01:00
5b32a402a6 02.02.26 2026-02-02 16:23:27 +01:00
10aaff4cc8 02.02.26 2026-02-02 16:08:35 +01:00
40025169d4 02.02.26 2026-02-02 16:04:07 +01:00
38a903469c 02.02.26 2026-02-02 15:59:20 +01:00
70a0a41787 02.02.26 2026-02-02 15:26:05 +01:00
318f9e5564 02.02.26 2026-02-02 15:16:37 +01:00
d73816285e 02.02.26 2026-02-02 12:50:57 +01:00
86ac172cfd 02.02.26 2026-02-02 12:33:54 +01:00
703c7e46fa 02.02.26 2026-02-02 12:26:58 +01:00
747c2e4a0e 02.02.26 2026-02-02 12:08:46 +01:00
7d89418874 02.02.26 2026-02-02 10:42:08 +01:00
64806b143b 02.02.26 2026-02-02 08:44:09 +01:00
7b669a27a8 02.02.26 2026-02-02 08:42:48 +01:00
62c2080fee 01.02.26 2026-02-01 21:36:39 +01:00
f5d6d4ef55 01.02.26 2026-02-01 20:58:57 +01:00
4513ce8ac7 01.02.26 2026-02-01 18:03:12 +01:00
88eb890c46 01.02.26 2026-02-01 17:55:54 +01:00
9a66952f50 30.01.26 2026-01-30 08:07:41 +01:00
d0db0abdd4 30.01.26 2026-01-30 07:47:15 +01:00
d9cb2b7961 30.01.26 2026-01-30 07:27:45 +01:00
3eedc0b30e 30.01.26 2026-01-30 06:31:16 +01:00
b51ee12331 29.01.26 2026-01-29 17:49:20 +01:00
744e147a1a 29.01.26 2026-01-29 17:23:40 +01:00
8cc0bffcae 29.01.26 2026-01-29 14:05:17 +01:00
2b3cf5c095 29.01.26 2026-01-29 13:17:11 +01:00
94f15d66da 29.01.26 2026-01-29 12:39:47 +01:00
8edd5ea901 29.01.26 2026-01-29 11:26:21 +01:00
1e23528258 29.01.26 2026-01-29 11:06:23 +01:00
9447a685f2 29.01.26 2026-01-29 10:31:42 +01:00
7155708d4b 28.01.26 2026-01-28 16:52:54 +01:00
a76b8798ae 28.01.26 2026-01-28 15:57:49 +01:00
016b92e5aa 28.01.26 2026-01-28 15:55:04 +01:00
6085879d6f 28.01.26 2026-01-28 15:51:43 +01:00
42652d0fcb 28.01.26 2026-01-28 15:39:15 +01:00
5e8917430b 28.01.26 2026-01-28 11:46:00 +01:00
cdcefe6fbc 28.01.26 2026-01-28 11:33:18 +01:00
0678263f3e 27.01.26 2026-01-27 23:15:45 +01:00
aa69b8157e 27.01.26 2026-01-27 23:03:54 +01:00
47ce23f4e8 27.01.26 2026-01-27 22:56:39 +01:00
f6fe3fb2bd 27.01.26 2026-01-27 22:50:34 +01:00
fabc380784 27.01.26 2026-01-27 22:42:31 +01:00
7c845d0f9a 27.01.26 2026-01-27 20:36:18 +01:00
50da3d7a68 27.01.26 2026-01-27 20:31:34 +01:00
da1b268862 27.01.26 2026-01-27 19:05:30 +01:00
73e46b399b 27.01.26 2026-01-27 18:54:53 +01:00
431062f123 27.01.26 2026-01-27 18:45:09 +01:00
3846932517 27.01.26 2026-01-27 18:29:50 +01:00
aa10a58b60 27.01.26 2026-01-27 18:21:00 +01:00
9db6d2cb45 27.01.26 2026-01-27 18:13:12 +01:00
509dcac22e 27.01.26 2026-01-27 18:07:41 +01:00
f3102ee835 27.01.26 2026-01-27 18:00:46 +01:00
4c3056326b 27.01.26 2026-01-27 17:56:54 +01:00
1339f51256 27.01.26 2026-01-27 17:47:37 +01:00
408c5d82e4 27.01.26 2026-01-27 17:26:42 +01:00
71a58814b4 27.01.26 2026-01-27 17:05:39 +01:00
870f2e93f4 27.01.26 2026-01-27 16:45:55 +01:00
842a2b1da6 27.01.27 2026-01-27 10:28:36 +01:00
bf7de1e5d1 26.01.26 2026-01-26 23:06:55 +01:00
ba0dd0e0fb 26.01.26 2026-01-26 23:03:52 +01:00
e0c7480dd2 26.01.26 2026-01-26 23:01:04 +01:00
9b324ca7cd 26.01.26 2026-01-26 22:57:35 +01:00
0cb824076b 26.01.26 2026-01-26 22:51:59 +01:00
02320ce9c8 26.01.26 2026-01-26 22:45:26 +01:00
6d82621a34 26.01.26 2026-01-26 22:41:31 +01:00
0dae857364 26.01.26 2026-01-26 22:38:27 +01:00
9a974365a7 26.01.26 2026-01-26 22:32:59 +01:00
e9e57803ae 26.01.26 2026-01-26 22:29:04 +01:00
a3b7d11ae3 26.01.26 2026-01-26 22:23:08 +01:00
2248f49f9d 26.01.26 2026-01-26 22:18:39 +01:00
f96e73d7b9 26.01.26 2026-01-26 22:15:30 +01:00
ece0e55f98 26.01.26 2026-01-26 22:09:09 +01:00
db207f4995 26.01.26 2026-01-26 22:05:58 +01:00
f94f8d3abe 26.01.26 2026-01-26 21:50:32 +01:00
017eb92c6a 26.01.26 2026-01-26 21:43:41 +01:00
765d526c16 26.01.26 2026-01-26 21:31:31 +01:00
e71c6b14d0 26.01.26 2026-01-26 21:26:39 +01:00
210adad3f6 26.01.26 2026-01-26 21:18:51 +01:00
72b1d4ffcd 26.01.26 2026-01-26 21:14:21 +01:00
e2fc7178c4 26.01.26 2026-01-26 21:06:23 +01:00
58b360ed6c 26.01.26 2026-01-26 21:00:36 +01:00
985f4dc19d 26.01.26 2026-01-26 20:48:41 +01:00
757b9f724d 26.01.26 2026-01-26 17:32:33 +01:00
fbfc943301 26.01.26 2026-01-26 13:16:42 +01:00
4c70f0bc5f 26.01.26 2026-01-26 12:42:49 +01:00
ef27c020e0 25.01.26 2026-01-25 14:41:47 +01:00
23d9d88492 22.01.26 2026-01-22 13:55:09 +01:00
fffbb9708a 20.01.26 2026-01-20 22:14:35 +01:00
e7ff1fd56c 20.01.26 2026-01-20 21:56:09 +01:00
5b962eeb1d 20.01.26 2026-01-20 21:38:23 +01:00
08581f24ea 20.01.26 2026-01-20 21:35:00 +01:00
5e9141f03a 20.01.26 2026-01-20 21:30:14 +01:00
2269fc7709 20.01.26 2026-01-20 20:59:36 +01:00
b966366bc4 20.01.26 2026-01-20 20:53:34 +01:00
e2b0695cb8 20.01.26 2026-01-20 20:48:21 +01:00
000547ae21 20.01.26 2026-01-20 20:42:59 +01:00
8cc19b67f9 20.01.26 2026-01-20 20:31:49 +01:00
fcb6377b21 20.01.26 2026-01-20 20:19:56 +01:00
8a54b6fcbc 20.01.26 2026-01-20 20:13:08 +01:00
ecdc4b283d 20.01.26 2026-01-20 19:46:48 +01:00
d9ab3cfe47 20.01.26 2026-01-20 19:36:31 +01:00
72fc0c5431 20.01.26 2026-01-20 19:15:22 +01:00
044096c8d7 20.01.26 2026-01-20 19:11:43 +01:00
96b6be3398 20.01.26 2026-01-20 19:08:19 +01:00
42fb2e0d22 20.01.26 2026-01-20 19:06:46 +01:00
227eb3cf46 20.01.26 2026-01-20 19:04:54 +01:00
0e007afed3 20.01.26 2026-01-20 19:02:44 +01:00
ddf023f078 20.01.26 2026-01-20 18:59:40 +01:00
a9d1fe7baf 20.01.26 2026-01-20 17:30:13 +01:00
aeadca6f60 20.01.26 2026-01-20 17:12:07 +01:00
17233d8ded 20.01.26 2026-01-20 17:02:55 +01:00
797783b59e 20.01.26 2026-01-20 16:42:52 +01:00
e5e76e2dcb 20.01.26 2026-01-20 16:13:52 +01:00
0fc80958af 20.01.26 2026-01-20 16:06:15 +01:00
b43ab082b4 20.01.26 2026-01-20 15:36:55 +01:00
fcf61605d6 20.01.26 2026-01-20 15:30:40 +01:00
c5053638df 20.01.26 2026-01-20 15:25:15 +01:00
c5fbd3e528 20.01.26 2026-01-20 14:57:16 +01:00
117c33a2ea 20.01.26 2026-01-20 14:55:04 +01:00
327de18f1a 20.01.26 2026-01-20 14:50:32 +01:00
437904bcd8 20.01.26 2026-01-20 13:08:52 +01:00
6d75b4660e 20.01.26 2026-01-20 11:56:38 +01:00
a110019345 20.01.26 2026-01-20 11:40:44 +01:00
da530a2211 20.01.26 2026-01-20 11:30:53 +01:00
51a512e568 20.01.26 2026-01-20 11:25:29 +01:00
5df6378e80 20.01.26 2026-01-20 11:14:09 +01:00
a479dc718b 20.01.26 2026-01-20 11:04:53 +01:00
c39bb1d6d1 18.01.26 2026-01-18 23:59:24 +01:00
5c1ca6a895 18.01.26 2026-01-18 23:55:08 +01:00
24c1186061 18.01.26 2026-01-18 23:30:49 +01:00
8ae4aeb3e8 18.01.26 2026-01-18 23:00:29 +01:00
9daa701a28 18.01.26 2026-01-18 22:23:09 +01:00
336c2415b3 18.01.26 2026-01-18 22:08:41 +01:00
49731501d6 18.01.26 2026-01-18 22:00:54 +01:00
661d38404a 18.01.26 2026-01-18 21:28:40 +01:00
e869338703 18.01.26 2026-01-18 21:22:41 +01:00
b55c1e3c8f 18.01.26 2026-01-18 21:12:27 +01:00
3820f00182 18.01.26 2026-01-18 21:07:55 +01:00
178fb25a2f 18.01.26 2026-01-18 20:52:20 +01:00
6b2d5aebf8 18.01.26 2026-01-18 20:02:49 +01:00
0d3bfda08c 18.01.26 2026-01-18 12:07:27 +01:00
11f33236cf 18.01.26 2026-01-18 11:05:00 +01:00
db7288c709 18.01.26 2026-01-18 10:51:40 +01:00
a31e51152b 18.01.26 2026-01-18 10:27:55 +01:00
97b8632b74 18.01.26 2026-01-18 10:18:57 +01:00
2ebbe334bf 18.01.26 2026-01-18 10:06:48 +01:00
7571a1cb80 17.01.26 2026-01-17 18:15:29 +01:00
b4f794c275 17.01.26 2026-01-17 17:55:07 +01:00
2f1adffba3 17.01.26 2026-01-17 17:44:01 +01:00
0047e6e879 17.01.26 2026-01-17 17:36:39 +01:00
e8453c76a7 17.01.26 2026-01-17 17:00:35 +01:00
5d7d0ffe5b 17.01.26 2026-01-17 16:54:51 +01:00
39c9d83f1b 17.01.26 2026-01-17 14:12:11 +01:00
b38f7553a6 17.01.26 2026-01-17 14:03:45 +01:00
9b887a6b4c 17.01.26 2026-01-17 09:59:48 +01:00
b43db20bc4 17.01.26 2026-01-17 09:53:19 +01:00
8e38ceb13e 17.01.26 2026-01-17 09:47:06 +01:00
18c3cf21c6 17.01.26 2026-01-17 09:36:45 +01:00
a73564a24d Merge pull request '17.01.26' (#6) from dev into main
Reviewed-on: https://srv413259.hstgr.cloud/admin/tradon/pulls/6
2026-01-17 08:26:19 +00:00
9c6029d152 17.01.26 2026-01-17 09:25:00 +01:00
7a35413da5 Merge pull request 'dev' (#5) from dev into main
Reviewed-on: https://srv413259.hstgr.cloud/admin/tradon/pulls/5
2026-01-17 05:48:03 +00:00
660e714983 16.01.26 2026-01-16 21:07:56 +01:00
9edfcd5058 Merge pull request 'main' (#4) from main into dev
Reviewed-on: https://srv413259.hstgr.cloud/admin/tradon/pulls/4
2026-01-16 08:15:31 +00:00
cd8f785881 13.01.26 2026-01-13 14:45:04 +01:00
24895173d6 11.01.26 2026-01-11 14:15:12 +01:00
6a1c26ce70 11.01.26 2026-01-11 11:11:55 +01:00
ba06fb60c2 11.01.26 2026-01-11 11:06:13 +01:00
3204eee7ac 10.01.26 2026-01-10 11:04:13 +01:00
815b8696d2 10.01.26 2026-01-10 10:58:17 +01:00
2e80c19e0e 10.01.26 2026-01-10 10:34:00 +01:00
170cc09627 Merge pull request 'Prod => Dev' (#3) from main into dev
Reviewed-on: https://srv413259.hstgr.cloud/admin/tradon/pulls/3
2026-01-10 09:18:03 +00:00
8fd8de607f 10.01.26 2026-01-10 09:22:47 +01:00
ebdfd7f499 09.01.26 2026-01-09 17:49:20 +01:00
19669281d5 07.01.26 2026-01-07 18:04:18 +01:00
ccbf545fe2 07.01.26 2026-01-07 17:59:01 +01:00
550ad57354 07.01.26 2026-01-07 16:17:21 +01:00
dbb488ba52 Merge pull request 'Dev adjusted by Prod' (#2) from main into dev
Reviewed-on: https://srv413259.hstgr.cloud/admin/tradon/pulls/2
2026-01-05 20:04:35 +00:00
49d2438ea0 05.01.26 2026-01-05 20:33:56 +01:00
92ba297bb7 05.01.26 2026-01-05 17:45:16 +01:00
cad3d04d08 Merge pull request 'Merge dev from main' (#1) from main into dev
Reviewed-on: https://srv413259.hstgr.cloud/admin/tradon/pulls/1
2026-01-05 13:03:45 +00:00
f1e002998e 05.01.26 2026-01-05 13:25:24 +01:00
c42edc4efd 05.01.26 2026-01-05 13:16:06 +01:00
e5056c6ea1 05.01.26 2026-01-05 13:09:58 +01:00
56a4bd9e82 04.01.26 2026-01-04 21:06:11 +01:00
9449f4f0ec 04.01.26 2026-01-04 20:15:27 +01:00
5111265019 04.01.26 2026-01-04 19:46:26 +01:00
750db2fe4d 04.01.26 2026-01-04 18:13:35 +01:00
36f9fb48a8 04.01.26 2026-01-04 18:09:22 +01:00
b857ab3eed 04.01.26 2026-01-04 18:07:36 +01:00
dfc2ea3a4e 04.01.26 2026-01-04 18:01:15 +01:00
dfc2ae4896 04.01.26 2026-01-04 17:30:28 +01:00
272b91609c 04.01.26 2026-01-04 17:27:49 +01:00
f47036b6df 04.01.26 2026-01-04 17:24:36 +01:00
bed9c9deac 04.01.26 2026-01-04 15:47:30 +01:00
837c75f8f0 04.01.26 2026-01-04 15:37:24 +01:00
dee3d2ff90 04.01.26 2026-01-04 14:49:22 +01:00
02b99b4622 04.01.26 2026-01-04 13:31:17 +01:00
37d73b6962 03.01.26 2026-01-03 17:17:25 +01:00
35c56538dd 03.01.26 2026-01-03 14:15:50 +01:00
13b7553641 03.01.26 2026-01-03 13:35:06 +01:00
bb117d4ee6 03.01.26 2026-01-03 13:25:11 +01:00
b0b2741422 29_12_25 2025-12-29 19:57:54 +01:00
ca83f0ec7b 29_12_25 2025-12-29 16:35:43 +01:00
1ab5e57017 28_12_25 2025-12-28 11:32:32 +01:00
a34f02db00 v1.0.1 2025-12-26 20:39:32 +01:00
568 changed files with 132147 additions and 4637 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.pyc
~$*
notes/accounting/excel_web_api/.env

162
AGENTS.md Normal file
View File

@@ -0,0 +1,162 @@
# AGENTS.md
Guide rapide pour les agents qui codent dans ce repository.
## 1) Contexte du projet
- Codebase Tryton monolithique (coeur + modules metier).
- Noyau serveur a la racine: `application.py`, `wsgi.py`, `admin.py`, `worker.py`, `cron.py`.
- Couches framework importantes:
- ORM: `model/`
- Meta/systeme (`ir`): `ir/`
- Protocoles RPC: `protocols/`
- Backend DB: `backend/`
- Modules metier: `modules/<module_name>/` (~220 modules).
## 2) Regles de travail pour agent
- Ne jamais toucher des fichiers sans rapport avec la demande.
- Limiter le scope de modif au minimum necessaire.
- Respecter le style existant du module cible.
- Ne pas supprimer du code legacy sans verifier les usages.
- Si comportement incertain: preferer un patch conservateur + test.
## 3) Zones de bruit a ignorer pendant l'exploration
- `.venv/`
- `__pycache__/`
- `build/` (quand present dans des sous-modules)
- Fichiers temporaires editeur (ex: `*.swp`)
## 4) Comment choisir ou coder selon le besoin
- Si bug ORM/champs:
- Lire `model/fields/*.py` et les tests `tests/test_field_*.py`.
- Si bug transaction/DB:
- Lire `transaction.py`, `backend/*/database.py`, `tests/test_backend.py`.
- Si bug API/RPC/HTTP:
- Lire `wsgi.py`, `rpc.py`, `protocols/*`, `tests/test_rpc.py`, `tests/test_wsgi.py`.
- Si bug metier:
- Modifier uniquement `modules/<module>/` + ses tests.
- Conventions de champs dates:
- Dans ce projet, ne pas introduire de `fields.DateTime`.
- Utiliser `fields.Date` pour les dates metier et les champs de suivi UI, sauf demande explicite deja existante dans le module cible.
- Si bug template Relatorio (`.fodt`):
- Lire d'abord le template standard voisin du meme domaine (`invoice.fodt`, `sale.fodt`, etc.).
- Preferer des proprietes Python simples exposees par le modele plutot que des expressions Genshi complexes dans le template.
- Dans les placeholders XML, utiliser `&quot;` et `&apos;` plutot que des antislashs type `\'`.
- Si un document facture depend fortement d'une vente/achat, ajouter au besoin un petit pont Python pour exposer des `report_*` stables au template.
- Pour les templates `stock.shipment.in`, preferer aussi des proprietes `report_*` sur le shipment plutot que des contextes ad hoc (`si_*`) quand le document devient metier ou client-specifique.
- Si plusieurs actions de report pointent vers `report_name = 'account.invoice'`, verifier aussi le cache `invoice_report_cache` dans `modules/account_invoice/invoice.py`: un mauvais cache peut faire croire que plusieurs actions utilisent le meme `.fodt`.
- Avant de conclure qu'un template ou une action est faux, verifier si le report alternatif doit bypasser le cache standard.
- Pour les templates shipment, ne pas supposer qu'une variable locale comme `shipment` sera definie partout dans Genshi, surtout dans les headers/footers; preferer `records[0]....` ou des placeholders alignes sur le scope reel du report.
- Dans `purchase_trade`, pour remonter d'une facture vers shipment, pro forma, freight ou autres donnees logistiques, privilegier le lot physique comme pont entre `purchase.line`, `sale.line` et shipment.
- Pour `FREIGHT VALUE`, ne pas lire un champ direct sur la facture: retrouver le fee de shipment (`shipment_in`) dont le produit est `Maritime freight`, puis utiliser `fee.get_amount()`.
- Rappels session templates (2026-04-08):
- `insurance.fodt`: le texte "insured for account of" doit afficher la compagnie courante (shipment.company.party), pas le client.
- `insurance.fodt`: exposer des proprietes Python `report_*` sur `stock.shipment.in` pour les montants (incoming moves) et les zones client-specifiques.
- `insurance.fodt`: "Amount insured" suit la regle metier 110% du montant incoming (base calculee via lot -> purchase.line.unit_price * quantite courante convertie).
- `insurance.fodt`: zone "Contact the following surveyor" alimentee par une propriete dediee, avec champ `surveyor` (party.party) cote shipment.
- `packing_list.fodt`: date en haut a droite = date du jour; unites Net/Gross = unite de `purchase.line`.
- `bill.fodt` (sale): la 2eme date doit etre une vraie maturity date (depuis `invoice.lines_to_pay.maturity_date`), pas `payment_term.rec_name`.
- `bill.fodt` (sale): le montant en lettres doit provenir du montant du bill (facture/total), pas du `unit_price` de ligne.
- Quand un template affiche les placeholders en brut, verifier que les champs sont bien des placeholders Relatorio dans le XML (pas du texte litteral).
- Eviter les apostrophes echappees style `\'` dans placeholders; preferer `&quot;` et `&apos;`.
## 4.bis) Memos metier et templates
- Regles metier transverses:
- `notes/business_rules.md`
- Regles metier locales `purchase_trade`:
- `modules/purchase_trade/docs/business-rules.md`
- `modules/purchase_trade/docs_source/business/` pour les sources de verite
des pages business publiees dans le wiki
- `modules/purchase_trade/docs/business/` pour les pages generees lues par le
wiki
- Decisions templates / reports:
- `notes/template_business_rules.md`
- Documentation comptable et reporting:
- `notes/accounting/README.md`
- `notes/accounting/gap_analysis_workflow.md`
- `notes/accounting/reporting.md`
- `notes/accounting/business_rules.md`
- `notes/accounting/purchase_trade_extensions.md`
- Regles sensibles `purchase_trade` a relire avant de toucher lots, quantites
ou fees:
- `modules/purchase_trade/AGENTS.md`
- `modules/purchase_trade/docs_source/business/lots-and-quantities.md`
- `modules/purchase_trade/docs/business-rules.md` BR-PT-020 / BR-PT-021
(`quantity_theorical`, `lot.qt`, lots physiques, fees et PnL fee).
## 5) Workflow de modification (obligatoire)
1. Identifier le module et le flux impacte.
2. Localiser un test existant proche du comportement a changer.
3. Implementer le plus petit patch possible.
4. Ajouter/adapter les tests au plus pres du changement.
5. Lancer la validation ciblee (pas toute la suite si inutile).
6. Donner un resume du risque residuel.
## 6) Checklist avant de rendre une modif
- Le changement est-il limite au domaine demande ?
- Le comportement existant non cible est-il preserve ?
- Les droits/regles (`ir.rule`, acces) sont-ils impactes ?
- Les vues XML et labels sont-ils coherents si un champ change ?
- Les tests modifies couvrent-ils le bug/la feature ?
- Le message de commit (si demande) explique clairement le pourquoi ?
## 7) Tests: point de depart pratique
- Suite coeur: `tests/test_tryton.py`
- Tests coeur par domaine: `tests/test_*.py`
- Tests module:
- `modules/<module>/tests/test_module.py`
- `modules/<module>/tests/test_scenario.py`
- `modules/<module>/tests/scenario_*.rst`
Quand possible, lancer d'abord la cible minimale:
- fichier de test touche
- puis fichier voisin de regression
- puis suite plus large uniquement si necessaire
## 8) Contrat de sortie attendu de l'agent
Toujours fournir:
- Liste des fichiers modifies
- Resume fonctionnel (ce qui change)
- Resume technique (pourquoi ce design)
- Tests executes + resultat
- Risques residuels et impacts potentiels
## 9) Cas sensibles (demander confirmation humaine)
- Changement schema/structure de donnees
- Changement de logique de securite/acces
- Changement de comportement transverse (transaction, pool, RPC, worker)
- Refactor multi-modules sans ticket explicite
## 10) Raccourci de demarrage pour agent
1. Lire ce fichier.
2. Lire le(s) fichier(s) touche(s) et leurs tests.
3. Proposer le patch minimal.
4. Implementer + tester cible.
5. Rendre avec le contrat de sortie (section 8).
- Rappels session 2026-04-09:
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: poids et unites depuis `lot.qt.hist` / `lot_unit_line`, priorite lots `physic`, sinon lot `virtual` unique.
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: infos shipment depuis les lots reels des lignes facture; ne rien afficher si plusieurs shipments differents.
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: `S/I` = `shipment.reference`; `NB BALES: 0` => `Unchanged` sur le final.
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: quantites uniformisees a `2` decimales; conversion `LBS` via UoM, jamais via un facteur fixe aveugle.
- `invoice_ict.fodt` / `invoice_ict_final.fodt`: si plusieurs lignes reutilisent le meme lot, les lignes detaillees suivent la quantite facturee convertie, mais le `GROSS` global doit rester le vrai delta historique du lot.
- `sale_ict.fodt`: meme priorite lots; les mots suivent l'unite reelle; le total convertit vers une unite commune, qui est celle du lot virtuel seulement s'il y a un seul lot virtuel sur tout le report.
- `lot.report.r_del_period`: utiliser `sale.line.del_period` pour `lot_s` sans `lot_p`, sinon `purchase.line.del_period`.
- `lot.do_weighing`: `lot_qt` editable et ecrasement direct de `lot.lot_qt`.
- `account.invoice`: `Validate` cree aussi le `account.move` pour les factures client, attribue aussi le `number` a ce stade pour les factures client comme fournisseur; `Post` ne doit plus forcer une fresh session sur ce flux.
- `pricing.pricing`: saisie manuelle autorisee meme sans composant; en manuel, l'utilisateur saisit seulement `Qt` et `Settl. price`; `fixed_qt`, `fixed_qt_price`, `unfixed_qt`, `unfixed_qt_price` et `eod_price` sont derives automatiquement.
- `pricing.pricing`: en manuel, `fixed_qt` = cumul des `quantity`, `fixed_qt_price` = moyenne ponderee cumulee des `settl_price`, `unfixed_qt` = reste a fixer, `unfixed_qt_price` = `settl_price` de la ligne.
- `pricing.pricing`: `eod_price` reste non editable et calcule en prix moyen pondere; `last=True` gere par groupe `line + component`, choisi sur la `pricing_date` la plus grande.
- `purchase_trade`: `trader` filtre sur `TRADER`, `operator` sur `OPERATOR`; fallback sur `quantity` si `quantity_theorical` est vide dans les quotas/pricings.
- `sale.line` / `purchase.line`: en mode `basis`, sans `price_component`, le `Price` et le `Fix. progress` de la ligne doivent remonter depuis la ligne `Summary` sans component.

1
debug.log Normal file
View File

@@ -0,0 +1 @@
[0407/143111.471:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: Accès refusé. (0x5)

5
deployment/README.md Normal file
View File

@@ -0,0 +1,5 @@
# Deployment Notes
- Runbook onboarding SSH: `deployment/runbooks/vps-onboarding.md`
- VPS 46.202.173.47 credentials: `deployment/vps/46.202.173.47-credentials.md`
- VPS 46.202.173.47 quickstart: `deployment/vps/46.202.173.47-quickstart.md`

View File

@@ -0,0 +1,57 @@
# Procedure - Ajouter un nouveau VPS (SSH)
Date de reference: 2026-04-07
## 1) Preparation locale (Windows)
1. Creer le dossier SSH local si absent:
`New-Item -ItemType Directory -Force $env:USERPROFILE\.ssh`
2. Generer une cle dediee VPS:
`ssh-keygen -t ed25519 -C "vps-deploy" -f $env:USERPROFILE\.ssh\vps_deploy_key`
3. Lire la cle publique:
`Get-Content $env:USERPROFILE\.ssh\vps_deploy_key.pub`
## 2) Installer la cle sur le VPS
1. Se connecter au VPS avec mot de passe (premiere fois):
`ssh <user>@<ip_vps>`
2. Sur le VPS, preparer le dossier SSH:
`mkdir -p ~/.ssh`
`chmod 700 ~/.ssh`
3. Ajouter la cle publique (une ligne complete):
`echo "ssh-ed25519 ... vps-deploy" >> ~/.ssh/authorized_keys`
`chmod 600 ~/.ssh/authorized_keys`
## 3) Tester la connexion par cle
Depuis Windows:
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps>`
## 4) Test operationnel minimal
Creer un dossier distant:
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps> "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
## 5) Durcissement recommande
- Desactiver l'authentification par mot de passe apres validation de la cle.
- Utiliser une cle dediee par environnement (dev/staging/prod).
- Documenter user + IP + chemin de cle dans une fiche VPS separee.
## 6) Note importante - Contrainte sandbox Codex
- Si une commande SSH/SCP echoue avec un message proche de:
- `Identity file ... not accessible: Permission denied`
- Cause probable:
- la session est en sandbox et ne peut pas lire la cle locale dans
`C:\Users\<user>\.ssh\...`.
- Action:
- relancer la commande en mode `require_escalated` (hors sandbox) pour
autoriser l'acces a la cle locale.
- Exemple observe le 2026-04-07:
- creation du dossier `/root/test` sur `46.202.173.47` reussie uniquement
apres escalation.

View File

@@ -0,0 +1,36 @@
# Fiche VPS - 46.202.173.47
Date de reference: 2026-04-07
## Identite serveur
- IP: `46.202.173.47`
- Hostname alias conseille: `vps3`
## Acces
- Cle publique Laurent Barontini (vps-deploy):
`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEm8JMCYsk6I1IoYhIHXNrdyERHdh+eeDCJagOHaRAEK vps-deploy`
- Cle publique Sylvain Duvernay (s.duvernay@singa-associates.com):
`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6Xsp/v6q6JO04ETv1880qoSPptUMxlWQvgcBz67o63 s.duvernay@singa-associates.com`
- Fichier local: `$env:USERPROFILE\.ssh\id_ed25519`
- Mot de passe fourni:
`!!OpenSquared!!`
- Utilisateur SSH:
'root'
- Port SSH:
'22'
## Commande de connexion type
- Laurent Barontini (cle vps-deploy):
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@46.202.173.47`
- Sylvain Duvernay (cle id_ed25519):
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47`
- Avec port custom:
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 -p <port> <user>@46.202.173.47`

View File

@@ -0,0 +1,39 @@
# Commandes Rapides - VPS 46.202.173.47
Date de reference: 2026-04-07
## Cles SSH par utilisateur
| Utilisateur | Fichier cle locale |
|---|---|
| Laurent Barontini | `$env:USERPROFILE\.ssh\vps_deploy_key` |
| Sylvain Duvernay | `$env:USERPROFILE\.ssh\id_ed25519` |
> Les commandes ci-dessous utilisent `id_ed25519` (Sylvain Duvernay).
## 1) Test SSH
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "echo ok"`
## 2) Creer un dossier test distant
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
## 3) Lister home distant
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "ls -la ~"`
## 4) Copier un fichier local vers le VPS
`scp -i $env:USERPROFILE\.ssh\id_ed25519 .\local.txt <user>@46.202.173.47:~/local.txt`
## 5) Recuperer un fichier du VPS
`scp -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47:~/remote.txt .\remote.txt`
## 6) Depannage sandbox (Codex)
- Symptome:
- `Identity file ... not accessible: Permission denied`
- Correctif:
- relancer la commande SSH/SCP en mode escalade (`require_escalated`).

View File

@@ -93,6 +93,7 @@ class Model(
cursor.execute(*ir_model.select(ir_model.id,
where=ir_model.model == model.__name__))
model_id = None
logger.info("MODEL_NAME:%s",model.__name__)
if cursor.rowcount == -1 or cursor.rowcount is None:
data = cursor.fetchone()
if data:

View File

@@ -73,6 +73,7 @@ attlist.field &=
| "text"
| "time"
| "timedelta"
| "tolerance_gauge"
| "url"
}?
attlist.field &= attribute fill { "0" | "1" }?
@@ -122,6 +123,12 @@ attlist.field &= attribute symbol { text }?
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
attlist.field &= attribute loading { "lazy" | "eager" }?
attlist.field &= attribute min { text }?
attlist.field &= attribute max { text }?
attlist.field &= attribute min_field { text }?
attlist.field &= attribute max_field { text }?
attlist.field &= attribute center { text }?
attlist.field &= attribute digits { text }?
image = element image { attlist.image, empty }
attlist.image &= attribute name { text }
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" }?

View File

@@ -251,6 +251,7 @@
<value>text</value>
<value>time</value>
<value>timedelta</value>
<value>tolerance_gauge</value>
<value>url</value>
</choice>
</attribute>
@@ -577,6 +578,54 @@
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">min</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">max</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">min_field</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">max_field</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">center</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">digits</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>

View File

@@ -42,6 +42,7 @@ attlist.field &=
| "text"
| "time"
| "timedelta"
| "tolerance_gauge"
| "url"
}?
attlist.field &=
@@ -75,6 +76,12 @@ attlist.field &= attribute help_field { text }?
attlist.field &= attribute view_ids { text }?
attlist.field &= attribute symbol { text }?
attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }?
attlist.field &= attribute min { text }?
attlist.field &= attribute max { text }?
attlist.field &= attribute min_field { text }?
attlist.field &= attribute max_field { text }?
attlist.field &= attribute center { text }?
attlist.field &= attribute digits { text }?
prefix = element prefix { attlist.affix, empty }
suffix = element suffix { attlist.affix, empty }
attlist.affix &= attribute string { text }?

View File

@@ -139,6 +139,7 @@
<value>text</value>
<value>time</value>
<value>timedelta</value>
<value>tolerance_gauge</value>
<value>url</value>
</choice>
</attribute>
@@ -330,6 +331,54 @@
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">min</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">max</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">min_field</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">max_field</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">center</name>
<text/>
</attribute>
</optional>
</define>
<define name="attlist.field" combine="interleave">
<optional>
<attribute>
<name ns="">digits</name>
<text/>
</attribute>
</optional>
</define>
<define name="prefix">
<element>
<name ns="">prefix</name>

View File

@@ -30,8 +30,8 @@ class Binary(Field):
on_change_with=None, depends=None, context=None, loading='lazy',
filename=None, file_id=None, store_prefix=None):
self.filename = filename
self.file_id = file_id
self.store_prefix = store_prefix
self.file_id = None #file_id
self.store_prefix = None #store_prefix
super(Binary, self).__init__(string=string, help=help,
required=required, readonly=readonly, domain=domain, states=states,
on_change=on_change, on_change_with=on_change_with,

View File

@@ -103,15 +103,26 @@ class Model(URLMixin, PoolBase, metaclass=ModelMeta):
@classmethod
def _get_name(cls):
'''
Returns the first non-empty line of the model docstring.
'''
assert cls.__doc__, '%s has no docstring' % cls
if cls.__doc__ is None:
print("\n💥 MODELE SANS DOCSTRING :", cls.__name__, " (module:", cls.__module__, ")")
raise Exception("MODELE SANS DOCSTRING")
lines = cls.__doc__.splitlines()
for line in lines:
line = line.strip()
if line:
return line
if lines:
return lines[0]
return cls.__name__
# @classmethod
# def _get_name(cls):
# '''
# Returns the first non-empty line of the model docstring.
# '''
# assert cls.__doc__, '%s has no docstring' % cls
# lines = cls.__doc__.splitlines()
# for line in lines:
# line = line.strip()
# if line:
# return line
@classmethod
def __register__(cls, module_name):

10
modules/AGENTS.md Normal file
View File

@@ -0,0 +1,10 @@
# AGENTS.md - Regles globales des modules
Ce guide complete le `AGENTS.md` racine et s'applique a tous les modules sous `modules/`.
## Regles transversales
- Ne pas introduire de `fields.DateTime` dans les modules metier sauf demande explicite et code cible deja base sur ce type.
- Pour afficher une date issue d'un champ technique comme `create_date`, preferer un champ fonctionnel en `fields.Date` plutot qu'un acces direct en vue.
- Si une vue doit afficher une information technique non declaree explicitement sur le modele, ajouter un champ fonctionnel dedie cote Python avant de modifier le XML.

View File

@@ -30,6 +30,8 @@ def register():
account.GeneralLedgerAccountParty,
account.GeneralLedgerLine,
account.GeneralLedgerLineContext,
account.GeneralLedgerDetail,
account.GeneralLedgerDetailContext,
account.BalanceSheetContext,
account.BalanceSheetComparisionContext,
account.IncomeStatementContext,
@@ -118,6 +120,7 @@ def register():
Pool.register(
account.AccountTypeStatement,
account.GeneralLedger,
account.GeneralLedgerDetailReport,
account.TrialBalance,
account.AgedBalanceReport,
move.GeneralJournal,

View File

@@ -7,9 +7,10 @@ from decimal import Decimal
from itertools import zip_longest
from dateutil.relativedelta import relativedelta
from sql import Column, Literal, Null, Window
from sql import Column, Literal, Null, Union, Window
from sql.aggregate import Count, Max, Min, Sum
from sql.conditionals import Case, Coalesce
from sql.operators import Concat
from trytond import backend
from trytond.i18n import gettext
@@ -2583,6 +2584,544 @@ class GeneralLedgerLineContext(GeneralLedgerAccountContext):
return super().get_context(fields_names=fields_names)
class GeneralLedgerDetailContext(GeneralLedgerAccountContext):
'General Ledger Detail Context'
__name__ = 'account.general_ledger.detail.context'
account = fields.Many2One(
'account.account', "Account",
domain=[
('company', '=', Eval('company')),
('type', '!=', None),
('closed', '!=', True),
],
depends=['company'])
account_from = fields.Char("Account From")
account_to = fields.Char("Account To")
currency = fields.Many2One('currency.currency', "Transaction Currency")
party = fields.Many2One(
'party.party', "Party",
context={
'company': Eval('company', -1),
},
depends=['company'])
document_number = fields.Char("Document Number")
@classmethod
def default_account(cls):
return Transaction().context.get('account')
@classmethod
def default_account_from(cls):
return Transaction().context.get('account_from')
@classmethod
def default_account_to(cls):
return Transaction().context.get('account_to')
@classmethod
def default_currency(cls):
return Transaction().context.get('currency')
@classmethod
def default_party(cls):
return Transaction().context.get('party')
@classmethod
def default_document_number(cls):
return Transaction().context.get('document_number')
@classmethod
def get_context(cls, fields_names=None):
fields_names = fields_names.copy() if fields_names is not None else []
fields_names += [
'account', 'account_from', 'account_to', 'currency', 'party',
'document_number']
return super().get_context(fields_names=fields_names)
class GeneralLedgerDetail(DescriptionOriginMixin, ModelSQL, ModelView):
'General Ledger Detail'
__name__ = 'account.general_ledger.detail'
row_type = fields.Selection([
('opening', "Opening"),
('movement', "Movement"),
('closing', "Closing"),
], "Row Type", sort=False)
row_sequence = fields.Integer("Row Sequence")
company = fields.Many2One('company.company', "Company")
account = fields.Many2One('account.account', "Account")
account_code = fields.Char("Account Code")
account_name = fields.Char("Account Name")
transaction_currency = fields.Many2One(
'currency.currency', "Transaction Currency")
base_currency = fields.Many2One('currency.currency', "Base Currency")
posting_date = fields.Date("Posting Date")
journal = fields.Many2One('account.journal', "Journal")
journal_entry_number = fields.Char("Journal Entry Number")
document_number = fields.Function(
fields.Char("Document Number"), 'get_document_field')
voucher_number = fields.Char("Voucher Number")
document_type = fields.Function(
fields.Char("Document Type"), 'get_document_field')
document_date = fields.Date("Document Date")
posting_status = fields.Selection([
('draft', "Draft"),
('posted', "Posted"),
], "Posting Status", sort=False)
party = fields.Many2One('party.party', "Party")
move = fields.Many2One('account.move', "Move")
move_line = fields.Many2One('account.move.line', "Move Line")
origin = fields.Reference('Origin', selection='get_origin')
description = fields.Char("Line Desc.")
move_description = fields.Char("Description")
reference = fields.Char("Reference")
debit_base_currency = fields.Numeric(
"Debit Base Currency", digits=(16, 2))
credit_base_currency = fields.Numeric(
"Credit Base Currency", digits=(16, 2))
balance_base_currency = fields.Numeric(
"Balance Base Currency", digits=(16, 2))
running_balance_base_currency = fields.Numeric(
"Running Balance Base Currency", digits=(16, 2))
debit_transaction_currency = fields.Numeric(
"Debit Transaction Currency", digits=(16, 2))
credit_transaction_currency = fields.Numeric(
"Credit Transaction Currency", digits=(16, 2))
balance_transaction_currency = fields.Numeric(
"Balance Transaction Currency", digits=(16, 2))
running_balance_transaction_currency = fields.Numeric(
"Running Balance Transaction Currency", digits=(16, 2))
payable_qty = fields.Numeric("Quantity", digits=(16, 5))
uom = fields.Char("UoM")
entered_by = fields.Many2One('res.user', "Entered By")
entered_date = fields.Date("Entered Date")
modified_by = fields.Many2One('res.user', "Modified By")
modified_date = fields.Date("Modified Date")
@classmethod
def __setup__(cls):
super().__setup__()
cls._order = [
('account_code', 'ASC'),
('transaction_currency', 'ASC'),
('row_sequence', 'ASC'),
('posting_date', 'ASC'),
('journal_entry_number', 'ASC'),
('id', 'ASC'),
]
@classmethod
def _get_period_ids(cls, name, context):
pool = Pool()
Period = pool.get('account.period')
period = None
if name == 'opening':
period_ids = []
if context.get('start_period'):
period = Period(context['start_period'])
elif name == 'closing':
period_ids = []
if context.get('end_period'):
period = Period(context['end_period'])
else:
periods = Period.search([
('fiscalyear', '=', context.get('fiscalyear')),
('type', '=', 'standard'),
],
order=[('start_date', 'DESC')], limit=1)
if periods:
period, = periods
else:
start_period_ids = set(cls._get_period_ids('opening', context))
end_period_ids = set(cls._get_period_ids('closing', context))
return list(end_period_ids.difference(start_period_ids))
if period:
if name == 'opening':
date_clause = ('end_date', '<=', period.start_date)
else:
date_clause = [
('end_date', '<=', period.end_date),
('start_date', '<', period.end_date),
]
periods = Period.search([
('fiscalyear', '=', context.get('fiscalyear')),
date_clause,
])
if period.start_date == period.end_date:
periods.append(period)
if periods:
period_ids = [p.id for p in periods]
if name == 'closing':
period_ids.append(period.id)
return period_ids
@classmethod
def _get_dates(cls, name, context):
if name == 'opening':
to_date = context.get('from_date') or datetime.date.min
if to_date:
try:
to_date -= datetime.timedelta(days=1)
except OverflowError:
pass
return None, to_date
elif name == 'closing':
return None, context.get('to_date')
return context.get('from_date'), context.get('to_date')
@classmethod
def _query_context(cls, name, context):
if context.get('start_period') or context.get('end_period'):
return {
'periods': cls._get_period_ids(name, context),
'from_date': None,
'to_date': None,
}
elif context.get('from_date') or context.get('to_date'):
from_date, to_date = cls._get_dates(name, context)
return {
'periods': None,
'from_date': from_date,
'to_date': to_date,
}
else:
if name == 'opening':
return {
'periods': [],
'from_date': None,
'to_date': None,
}
return {
'periods': None,
'from_date': None,
'to_date': None,
}
@classmethod
def _extra_where(cls, line, move, account, tx_currency, context):
where = (
(move.company == context.get('company'))
& (account.type != Null)
& (account.closed != Literal(True)))
if context.get('account'):
where &= line.account == context['account']
if context.get('account_from'):
where &= account.code >= context['account_from']
if context.get('account_to'):
where &= account.code <= context['account_to']
if context.get('currency'):
where &= tx_currency == context['currency']
if context.get('party'):
where &= line.party == context['party']
if context.get('document_number'):
value = '%%%s%%' % context['document_number']
where &= (
move.number.ilike(value)
| move.post_number.ilike(value)
| move.ext_ref.ilike(value))
return where
@classmethod
def _tables(cls):
pool = Pool()
Line = pool.get('account.move.line')
Move = pool.get('account.move')
Account = pool.get('account.account')
Company = pool.get('company.company')
line = Line.__table__()
move = Move.__table__()
account = Account.__table__()
company = Company.__table__()
return Line, line, move, account, company
@classmethod
def _lot_tables(cls, Line):
if 'lot' not in Line._fields:
return None, None
pool = Pool()
try:
Lot = pool.get('lot.lot')
Uom = pool.get('product.uom')
except KeyError:
return None, None
return Lot.__table__(), Uom.__table__()
@classmethod
def _account_label(cls, account):
return Concat(
Concat(Coalesce(account.code, ''), ' - '),
Coalesce(account.name, ''))
@classmethod
def _opening_balance_query(cls, context):
Line, line, move, account, company = cls._tables()
tx_currency = Coalesce(line.second_currency, company.currency)
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
tx_amount = Case(
(line.second_currency != Null,
Coalesce(line.amount_second_currency, 0)),
else_=base_amount)
with Transaction().set_context(
context, **cls._query_context('opening', context)):
line_query, _ = Line.query_get(line)
where = line_query & cls._extra_where(
line, move, account, tx_currency, context)
return (line.join(move, condition=line.move == move.id)
.join(account, condition=line.account == account.id)
.join(company, condition=move.company == company.id)
.select(
line.account.as_('account'),
tx_currency.as_('transaction_currency'),
Sum(base_amount).as_('base_amount'),
Sum(tx_amount).as_('transaction_amount'),
where=where,
group_by=[line.account, tx_currency],
having=Sum(base_amount) != 0))
@classmethod
def _line_query(cls, name, context):
Line, line, move, account, company = cls._tables()
lot, uom = cls._lot_tables(Line)
opening_balance = cls._opening_balance_query(context)
account_label = cls._account_label(account)
quantity = Literal(None)
unit = Literal(None)
tx_currency = Coalesce(line.second_currency, company.currency)
from_ = (line.join(move, condition=line.move == move.id)
.join(account, condition=line.account == account.id)
.join(company, condition=move.company == company.id))
from_ = from_.join(opening_balance, 'LEFT',
condition=(
(opening_balance.account == line.account)
& (opening_balance.transaction_currency == tx_currency)))
if lot is not None:
from_ = (from_
.join(lot, 'LEFT', condition=Column(line, 'lot') == lot.id)
.join(uom, 'LEFT', condition=lot.lot_unit_line == uom.id))
quantity = lot.lot_qt
unit = uom.symbol
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
second_amount = Coalesce(line.amount_second_currency, 0)
tx_amount = Case(
(line.second_currency != Null, second_amount),
else_=base_amount)
debit_tx = Case(
(line.second_currency == Null, Coalesce(line.debit, 0)),
(second_amount > 0, second_amount),
else_=Literal(0))
credit_tx = Case(
(line.second_currency == Null, Coalesce(line.credit, 0)),
(second_amount < 0, -second_amount),
else_=Literal(0))
with Transaction().set_context(
context, **cls._query_context(name, context)):
line_query, _ = Line.query_get(line)
where = line_query & cls._extra_where(
line, move, account, tx_currency, context)
window = Window(
[line.account, tx_currency],
order_by=[move.date.asc, move.number.asc, line.id.asc])
return from_.select(
(line.id * 3).as_('id'),
line.create_uid.as_('create_uid'),
line.create_date.as_('create_date'),
line.write_uid.as_('write_uid'),
line.write_date.as_('write_date'),
Literal('movement').as_('row_type'),
Literal(1).as_('row_sequence'),
move.company.as_('company'),
line.account.as_('account'),
account_label.as_('account_code'),
account.name.as_('account_name'),
tx_currency.as_('transaction_currency'),
company.currency.as_('base_currency'),
move.date.as_('posting_date'),
move.journal.as_('journal'),
move.number.as_('journal_entry_number'),
move.post_number.as_('voucher_number'),
move.date.as_('document_date'),
move.state.as_('posting_status'),
line.party.as_('party'),
line.move.as_('move'),
line.id.as_('move_line'),
move.origin.as_('origin'),
line.description.as_('description'),
move.description.as_('move_description'),
Coalesce(move.ext_ref, move.description).as_('reference'),
line.debit.as_('debit_base_currency'),
line.credit.as_('credit_base_currency'),
base_amount.as_('balance_base_currency'),
(Coalesce(opening_balance.base_amount, 0)
+ Sum(base_amount, window=window)).as_(
'running_balance_base_currency'),
debit_tx.as_('debit_transaction_currency'),
credit_tx.as_('credit_transaction_currency'),
tx_amount.as_('balance_transaction_currency'),
(Coalesce(opening_balance.transaction_amount, 0)
+ Sum(tx_amount, window=window)).as_(
'running_balance_transaction_currency'),
quantity.as_('payable_qty'),
unit.as_('uom'),
line.create_uid.as_('entered_by'),
line.create_date.as_('entered_date'),
line.write_uid.as_('modified_by'),
line.write_date.as_('modified_date'),
where=where)
@classmethod
def _summary_query(cls, name, context):
Line, line, move, account, company = cls._tables()
account_label = cls._account_label(account)
tx_currency = Coalesce(line.second_currency, company.currency)
base_amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
tx_amount = Case(
(line.second_currency != Null,
Coalesce(line.amount_second_currency, 0)),
else_=base_amount)
row_sequence = 0 if name == 'opening' else 2
row_id = (
(Min(line.id) * 3 - 1) if name == 'opening'
else (Min(line.id) * 3 - 2))
with Transaction().set_context(
context, **cls._query_context(name, context)):
line_query, _ = Line.query_get(line)
where = line_query & cls._extra_where(
line, move, account, tx_currency, context)
return (line.join(move, condition=line.move == move.id)
.join(account, condition=line.account == account.id)
.join(company, condition=move.company == company.id)
.select(
row_id.as_('id'),
Literal(0).as_('create_uid'),
Max(line.create_date).as_('create_date'),
Literal(0).as_('write_uid'),
Max(line.write_date).as_('write_date'),
Literal(name).as_('row_type'),
Literal(row_sequence).as_('row_sequence'),
move.company.as_('company'),
line.account.as_('account'),
account_label.as_('account_code'),
account.name.as_('account_name'),
tx_currency.as_('transaction_currency'),
company.currency.as_('base_currency'),
Literal(None).as_('posting_date'),
Literal(None).as_('journal'),
Literal(None).as_('journal_entry_number'),
Literal(None).as_('voucher_number'),
Literal(None).as_('document_date'),
Literal(None).as_('posting_status'),
Literal(None).as_('party'),
Literal(None).as_('move'),
Literal(None).as_('move_line'),
Literal(None).as_('origin'),
Literal(name.title()).as_('description'),
Literal(None).as_('move_description'),
Literal(None).as_('reference'),
Literal(0).as_('debit_base_currency'),
Literal(0).as_('credit_base_currency'),
Sum(base_amount).as_('balance_base_currency'),
Sum(base_amount).as_('running_balance_base_currency'),
Literal(0).as_('debit_transaction_currency'),
Literal(0).as_('credit_transaction_currency'),
Sum(tx_amount).as_('balance_transaction_currency'),
Sum(tx_amount).as_('running_balance_transaction_currency'),
Literal(None).as_('payable_qty'),
Literal(None).as_('uom'),
Literal(None).as_('entered_by'),
Literal(None).as_('entered_date'),
Literal(None).as_('modified_by'),
Literal(None).as_('modified_date'),
where=where,
group_by=[
move.company, line.account, account.code, account.name,
tx_currency, company.currency],
having=Sum(base_amount) != 0))
@classmethod
def table_query(cls):
pool = Pool()
DetailContext = pool.get('account.general_ledger.detail.context')
context = DetailContext.get_context()
return Union(
cls._summary_query('opening', context),
cls._line_query('movement', context),
cls._summary_query('closing', context))
@classmethod
def get_origin(cls):
Move = Pool().get('account.move')
return Move.get_origin()
def _get_related_payment(self):
if not self.move_line:
return None
try:
Payment = Pool().get('account.payment')
except KeyError:
return None
payments = Payment.search([('line', '=', self.move_line.id)], limit=1)
return payments[0] if payments else None
def get_document_field(self, name):
origin = self.move.origin if self.move else None
if origin:
model_name = origin.__name__
if model_name == 'account.invoice':
if name == 'document_number':
return origin.number or origin.reference
side = {
'out': 'Customer',
'in': 'Supplier',
}.get(origin.type, '')
stage = ''
reference = (origin.reference or '').lower()
if 'provisional' in reference:
stage = 'Provisional'
elif 'final' in reference:
stage = 'Final'
return ' '.join(filter(None, ['Invoice', side, stage]))
if model_name == 'account.payment':
if name == 'document_number':
return origin.number
side = {
'receivable': 'Customer',
'payable': 'Supplier',
}.get(origin.kind, '')
return ' '.join(filter(None, ['Payment', side]))
if name == 'document_number':
return getattr(origin, 'number', None) or getattr(
origin, 'reference', None)
return getattr(origin, 'rec_name', None) or model_name
payment = self._get_related_payment()
if payment:
if name == 'document_number':
return payment.number
side = {
'receivable': 'Customer',
'payable': 'Supplier',
}.get(payment.kind, '')
return ' '.join(filter(None, ['Payment', side]))
return None
class GeneralLedger(Report):
__name__ = 'account.general_ledger'
@@ -2611,6 +3150,52 @@ class GeneralLedger(Report):
return report_context
class GeneralLedgerDetailReport(Report):
__name__ = 'account.general_ledger.detail.report'
@classmethod
def get_context(cls, records, header, data):
pool = Pool()
Company = pool.get('company.company')
Fiscalyear = pool.get('account.fiscalyear')
Period = pool.get('account.period')
context = Transaction().context
report_context = super().get_context(records, header, data)
report_context['company'] = Company(context['company'])
report_context['fiscalyear'] = Fiscalyear(context['fiscalyear'])
for period in ['start_period', 'end_period']:
if context.get(period):
report_context[period] = Period(context[period])
else:
report_context[period] = None
report_context['from_date'] = context.get('from_date')
report_context['to_date'] = context.get('to_date')
report_context['groups'] = cls._groups(records)
return report_context
@classmethod
def _groups(cls, records):
groups = []
current_key = None
current = None
for line in records:
currency = getattr(line, 'transaction_currency', None)
key = (
getattr(line, 'account_code', None),
getattr(currency, 'id', None))
if key != current_key:
current = {
'account': line.account_code or line.account_name or '',
'currency': currency,
'lines': [],
}
groups.append(current)
current_key = key
current['lines'].append(line)
return groups
class TrialBalance(Report):
__name__ = 'account.trial_balance'

View File

@@ -806,6 +806,70 @@ this repository contains the full copyright notices and license terms. -->
<field name="rule_group" ref="rule_group_general_ledger_line_companies"/>
</record>
<record model="ir.ui.view" id="general_ledger_detail_view_list">
<field name="model">account.general_ledger.detail</field>
<field name="type">tree</field>
<field name="name">general_ledger_detail_list</field>
</record>
<record model="ir.action.act_window" id="act_general_ledger_detail_form">
<field name="name">General Ledger Detail</field>
<field name="res_model">account.general_ledger.detail</field>
<field name="context_model">account.general_ledger.detail.context</field>
</record>
<record model="ir.action.act_window.view"
id="act_general_ledger_detail_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="general_ledger_detail_view_list"/>
<field name="act_window" ref="act_general_ledger_detail_form"/>
</record>
<record model="ir.action.report" id="report_general_ledger_detail">
<field name="name">General Ledger Detail</field>
<field name="records">listed</field>
<field name="model">account.general_ledger.detail</field>
<field name="report_name">account.general_ledger.detail.report</field>
<field name="report">account/general_ledger_detail.fodt</field>
</record>
<record model="ir.action.keyword" id="report_general_ledger_detail_keyword">
<field name="keyword">form_print</field>
<field name="model">account.general_ledger.detail,-1</field>
<field name="action" ref="report_general_ledger_detail"/>
</record>
<menuitem
name="General Ledger Detail"
parent="menu_reporting"
action="act_general_ledger_detail_form"
sequence="11"
id="menu_general_ledger_detail"/>
<record model="ir.model.access" id="access_general_ledger_detail">
<field name="model">account.general_ledger.detail</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_general_ledger_detail_account">
<field name="model">account.general_ledger.detail</field>
<field name="group" ref="group_account"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.rule.group" id="rule_group_general_ledger_detail_companies">
<field name="name">User in companies</field>
<field name="model">account.general_ledger.detail</field>
<field name="global_p" eval="True"/>
</record>
<record model="ir.rule" id="rule_general_ledger_detail_companies">
<field name="domain"
eval="[('company', 'in', Eval('companies', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_general_ledger_detail_companies"/>
</record>
<record model="ir.ui.view" id="general_ledger_account_context_view_form">
<field name="model">account.general_ledger.account.context</field>
<field name="type">form</field>
@@ -817,6 +881,11 @@ this repository contains the full copyright notices and license terms. -->
<field name="inherit" ref="general_ledger_account_context_view_form"/>
<field name="name">general_ledger_line_context_form</field>
</record>
<record model="ir.ui.view" id="general_ledger_detail_context_view_form">
<field name="model">account.general_ledger.detail.context</field>
<field name="type">form</field>
<field name="name">general_ledger_detail_context_form</field>
</record>
<record model="ir.ui.view" id="account_balance_sheet_view_tree">
<field name="model">account.account.type</field>

View File

@@ -0,0 +1,825 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:officeooo="http://openoffice.org/2009/office" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta><meta:generator>LibreOffice/7.3.5.2$Linux_X86_64 LibreOffice_project/30$Build-2</meta:generator><meta:creation-date>2008-06-07T15:26:41</meta:creation-date><dc:date>2008-08-27T17:03:36</dc:date><meta:editing-cycles>1</meta:editing-cycles><meta:editing-duration>PT0S</meta:editing-duration><meta:document-statistic meta:table-count="4" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="51" meta:word-count="145" meta:character-count="1808" meta:non-whitespace-character-count="1714"/><meta:user-defined meta:name="Info 1"/><meta:user-defined meta:name="Info 2"/><meta:user-defined meta:name="Info 3"/><meta:user-defined meta:name="Info 4"/></office:meta>
<office:settings>
<config:config-item-set config:name="ooo:view-settings">
<config:config-item config:name="ViewAreaTop" config:type="long">24130</config:config-item>
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
<config:config-item config:name="ViewAreaWidth" config:type="long">48129</config:config-item>
<config:config-item config:name="ViewAreaHeight" config:type="long">22994</config:config-item>
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
<config:config-item-map-indexed config:name="Views">
<config:config-item-map-entry>
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
<config:config-item config:name="ViewLeft" config:type="long">19627</config:config-item>
<config:config-item config:name="ViewTop" config:type="long">29242</config:config-item>
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
<config:config-item config:name="VisibleTop" config:type="long">24130</config:config-item>
<config:config-item config:name="VisibleRight" config:type="long">48128</config:config-item>
<config:config-item config:name="VisibleBottom" config:type="long">47122</config:config-item>
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutColumns" config:type="short">0</config:config-item>
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
<config:config-item config:name="ZoomFactor" config:type="short">100</config:config-item>
<config:config-item config:name="IsSelectedFrame" config:type="boolean">false</config:config-item>
<config:config-item config:name="KeepRatio" config:type="boolean">false</config:config-item>
<config:config-item config:name="AnchoredTextOverflowLegacy" config:type="boolean">false</config:config-item>
</config:config-item-map-entry>
</config:config-item-map-indexed>
</config:config-item-set>
<config:config-item-set config:name="ooo:configuration-settings">
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectFields" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectBookmarks" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">true</config:config-item>
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">false</config:config-item>
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="GutterAtTop" config:type="boolean">false</config:config-item>
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="ContinuousEndnotes" config:type="boolean">false</config:config-item>
<config:config-item config:name="EmbedFonts" config:type="boolean">false</config:config-item>
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
<config:config-item config:name="HeaderSpacingBelowLastPara" config:type="boolean">false</config:config-item>
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
<config:config-item config:name="FrameAutowidthWithMorePara" config:type="boolean">false</config:config-item>
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
<config:config-item config:name="ApplyUserData" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintFaxName" config:type="string"/>
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
<config:config-item config:name="Rsid" config:type="int">4069242</config:config-item>
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="FootnoteInColumnToPageEnd" config:type="boolean">true</config:config-item>
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
<config:config-item config:name="PrinterName" config:type="string"/>
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddParaLineSpacingToTableCells" config:type="boolean">false</config:config-item>
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="TabOverSpacing" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
<config:config-item config:name="PrintEmptyPages" config:type="boolean">true</config:config-item>
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
<config:config-item config:name="RsidRoot" config:type="int">2070049</config:config-item>
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
<config:config-item config:name="MsWordCompMinLineHeightByFly" config:type="boolean">false</config:config-item>
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">false</config:config-item>
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">true</config:config-item>
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
<config:config-item config:name="TabOverflow" config:type="boolean">false</config:config-item>
</config:config-item-set>
</office:settings>
<office:scripts>
<office:script script:language="ooo:Basic">
<ooo:libraries xmlns:ooo="http://openoffice.org/2004/office" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</office:script>
</office:scripts>
<office:font-face-decls>
<style:font-face style:name="Andale Sans UI" svg:font-family="&apos;Andale Sans UI&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="DejaVu Sans" svg:font-family="&apos;DejaVu Sans&apos;" style:font-family-generic="system" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-adornments="Regular" style:font-family-generic="swiss" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif1" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Bold" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="Liberation Serif2" svg:font-family="&apos;Liberation Serif&apos;" style:font-adornments="Regular" style:font-family-generic="roman" style:font-pitch="variable"/>
<style:font-face style:name="StarSymbol" svg:font-family="StarSymbol"/>
<style:font-face style:name="Thorndale AMT" svg:font-family="&apos;Thorndale AMT&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>
</office:font-face-decls>
<office:styles>
<style:default-style style:family="graphic">
<style:graphic-properties svg:stroke-color="#000000" draw:fill-color="#99ccff" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/>
<style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" style:writing-mode="lr-tb" style:font-independent-line-spacing="false">
<style:tab-stops/>
</style:paragraph-properties>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none"/>
</style:default-style>
<style:default-style style:family="paragraph">
<style:paragraph-properties fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="lr-tb"/>
<style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Thorndale AMT" fo:font-size="12pt" fo:language="en" fo:country="US" style:letter-kerning="true" style:font-name-asian="Andale Sans UI" style:font-size-asian="10.5pt" style:language-asian="zxx" style:country-asian="none" style:font-name-complex="Andale Sans UI" style:font-size-complex="12pt" style:language-complex="zxx" style:country-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false"/>
</style:default-style>
<style:default-style style:family="table">
<style:table-properties table:border-model="collapsing"/>
</style:default-style>
<style:default-style style:family="table-row">
<style:table-row-properties fo:keep-together="auto"/>
</style:default-style>
<style:style style:name="Standard" style:family="paragraph" style:class="text">
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:keep-with-next="always"/>
<style:text-properties style:font-name="Liberation Serif2" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Regular" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="16pt" style:font-name-asian="DejaVu Sans" style:font-family-asian="&apos;DejaVu Sans&apos;" style:font-family-generic-asian="system" style:font-pitch-asian="variable" style:font-size-asian="14pt" style:font-name-complex="DejaVu Sans" style:font-family-complex="&apos;DejaVu Sans&apos;" style:font-family-generic-complex="system" style:font-pitch-complex="variable" style:font-size-complex="14pt"/>
</style:style>
<style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard" style:class="text">
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false"/>
<style:text-properties style:font-name="Liberation Sans" fo:font-family="&apos;Liberation Sans&apos;" style:font-style-name="Regular" style:font-family-generic="swiss" style:font-pitch="variable" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="List" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="list">
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Caption" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties fo:margin-top="0.212cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" text:number-lines="false" text:line-number="0"/>
<style:text-properties fo:font-size="12pt" fo:font-style="italic" style:font-size-asian="12pt" style:font-style-asian="italic" style:font-size-complex="12pt" style:font-style-complex="italic"/>
</style:style>
<style:style style:name="Index" style:family="paragraph" style:parent-style-name="Standard" style:class="index">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-size-asian="12pt"/>
</style:style>
<style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0"/>
<style:text-properties fo:font-size="11pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="16pt" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Table_20_Heading" style:display-name="Table Heading" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:class="extra">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" text:number-lines="false" text:line-number="0"/>
<style:text-properties style:font-name="Liberation Serif1" fo:font-family="&apos;Liberation Serif&apos;" style:font-style-name="Bold" style:font-family-generic="roman" style:font-pitch="variable" fo:font-size="12pt" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Horizontal_20_Line" style:display-name="Horizontal Line" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="html">
<style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" style:border-line-width-bottom="0.002cm 0.035cm 0.002cm" fo:padding="0cm" fo:border-left="none" fo:border-right="none" fo:border-top="none" fo:border-bottom="1.11pt double #808080" text:number-lines="false" text:line-number="0" style:join-border="false"/>
<style:text-properties fo:font-size="6pt" style:font-size-asian="6pt" style:font-size-complex="6pt"/>
</style:style>
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.5cm" style:type="center"/>
<style:tab-stop style:position="17cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
</style:style>
<style:style style:name="Header" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra">
<style:paragraph-properties text:number-lines="false" text:line-number="0">
<style:tab-stops>
<style:tab-stop style:position="8.795cm" style:type="center"/>
<style:tab-stop style:position="17.59cm" style:type="right"/>
</style:tab-stops>
</style:paragraph-properties>
<style:text-properties fo:font-size="9pt" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="text">
<style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Text_20_body_20_indent" style:display-name="Text body indent" style:family="paragraph" style:parent-style-name="Text_20_body" style:class="text">
<style:paragraph-properties fo:margin-left="0.499cm" fo:margin-right="0cm" fo:margin-top="0cm" fo:margin-bottom="0cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Quotations" style:family="paragraph" style:parent-style-name="Standard" style:class="html">
<style:paragraph-properties fo:margin-left="1cm" fo:margin-right="1cm" fo:margin-top="0cm" fo:margin-bottom="0.499cm" style:contextual-spacing="false" fo:text-indent="0cm" style:auto-text-indent="false"/>
</style:style>
<style:style style:name="Title" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="28pt" fo:font-weight="bold" style:font-size-asian="28pt" style:font-weight-asian="bold" style:font-size-complex="28pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="Subtitle" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
<style:paragraph-properties fo:margin-top="0.106cm" fo:margin-bottom="0.212cm" style:contextual-spacing="false" fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties fo:font-size="18pt" style:font-size-asian="18pt" style:font-size-complex="18pt"/>
</style:style>
<style:style style:name="Placeholder" style:family="text">
<style:text-properties fo:font-variant="small-caps" fo:color="#008080" loext:opacity="100%" style:text-underline-style="dotted" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="Bullet_20_Symbols" style:display-name="Bullet Symbols" style:family="text">
<style:text-properties style:font-name="StarSymbol" fo:font-family="StarSymbol" fo:font-size="9pt" style:font-name-asian="StarSymbol" style:font-family-asian="StarSymbol" style:font-size-asian="9pt" style:font-name-complex="StarSymbol" style:font-family-complex="StarSymbol" style:font-size-complex="9pt"/>
</style:style>
<text:outline-style style:name="Outline">
<text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
<text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format="">
<style:list-level-properties text:min-label-distance="0.381cm"/>
</text:outline-level-style>
</text:outline-style>
<text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/>
<text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/>
<text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/>
<style:style style:name="Default_20_Style.1" style:display-name="Default Style.1" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.2" style:display-name="Default Style.2" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.3" style:display-name="Default Style.3" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.4" style:display-name="Default Style.4" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.5" style:display-name="Default Style.5" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.6" style:display-name="Default Style.6" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.7" style:display-name="Default Style.7" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.8" style:display-name="Default Style.8" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.9" style:display-name="Default Style.9" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.10" style:display-name="Default Style.10" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.11" style:display-name="Default Style.11" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.12" style:display-name="Default Style.12" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.13" style:display-name="Default Style.13" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.14" style:display-name="Default Style.14" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-right="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.15" style:display-name="Default Style.15" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-top="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<style:style style:name="Default_20_Style.16" style:display-name="Default Style.16" style:family="table-cell">
<style:table-cell-properties fo:border-left="0.51pt solid #000000" fo:border-bottom="0.51pt solid #000000"/>
</style:style>
<table:table-template table:name="Default Style" table:first-row-end-column="row" table:first-row-start-column="row" table:last-row-end-column="row" table:last-row-start-column="row">
<table:first-row table:style-name="Default_20_Style.1"/>
<table:last-row table:style-name="Default_20_Style.2"/>
<table:first-column table:style-name="Default_20_Style.3"/>
<table:last-column table:style-name="Default_20_Style.4"/>
<table:body table:style-name="Default_20_Style.9"/>
<table:even-rows table:style-name="Default_20_Style.5"/>
<table:odd-rows table:style-name="Default_20_Style.6"/>
<table:even-columns table:style-name="Default_20_Style.7"/>
<table:odd-columns table:style-name="Default_20_Style.8"/>
<table:background table:style-name="Default_20_Style.10"/>
<loext:first-row-even-column table:style-name="Default_20_Style.15"/>
<loext:last-row-even-column table:style-name="Default_20_Style.16"/>
<loext:first-row-end-column table:style-name="Default_20_Style.12"/>
<loext:first-row-start-column table:style-name="Default_20_Style.11"/>
<loext:last-row-end-column table:style-name="Default_20_Style.14"/>
<loext:last-row-start-column table:style-name="Default_20_Style.13"/>
</table:table-template>
</office:styles>
<office:automatic-styles>
<style:style style:name="Table2" style:family="table">
<style:table-properties style:width="23.94cm" table:align="margins"/>
</style:style>
<style:style style:name="Table2.A" style:family="table-column">
<style:table-column-properties style:column-width="7.98cm" style:rel-column-width="21845*"/>
</style:style>
<style:style style:name="Table2.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="transparent" fo:padding="0.097cm" fo:border="none">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1" style:family="table">
<style:table-properties style:width="23.94cm" table:align="margins"/>
</style:style>
<style:style style:name="Table1.A" style:family="table-column">
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32768*"/>
</style:style>
<style:style style:name="Table1.B" style:family="table-column">
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32767*"/>
</style:style>
<style:style style:name="Table1.A1" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border="none"/>
</style:style>
<style:style style:name="Table2" style:family="table">
<style:table-properties style:width="23.94cm" table:align="margins"/>
</style:style>
<style:style style:name="Table2.A" style:family="table-column">
<style:table-column-properties style:column-width="7.98cm" style:rel-column-width="21845*"/>
</style:style>
<style:style style:name="Table2.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="transparent" fo:padding="0.097cm" fo:border="none">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table1" style:family="table">
<style:table-properties style:width="23.94cm" table:align="margins"/>
</style:style>
<style:style style:name="Table1.A" style:family="table-column">
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32768*"/>
</style:style>
<style:style style:name="Table1.B" style:family="table-column">
<style:table-column-properties style:column-width="11.97cm" style:rel-column-width="32767*"/>
</style:style>
<style:style style:name="Table1.A1" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border="none"/>
</style:style>
<style:style style:name="Lines" style:family="table">
<style:table-properties style:width="23.94cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:break-before="auto" fo:break-after="auto" table:align="margins" fo:background-color="transparent" fo:keep-with-next="always" style:may-break-between-rows="true" style:writing-mode="lr-tb">
<style:background-image/>
</style:table-properties>
</style:style>
<style:style style:name="Lines.A" style:family="table-column">
<style:table-column-properties style:column-width="2.499cm" style:rel-column-width="1417*"/>
</style:style>
<style:style style:name="Lines.B" style:family="table-column">
<style:table-column-properties style:column-width="3.358cm" style:rel-column-width="1904*"/>
</style:style>
<style:style style:name="Lines.C" style:family="table-column">
<style:table-column-properties style:column-width="3.36cm" style:rel-column-width="1905*"/>
</style:style>
<style:style style:name="Lines.F" style:family="table-column">
<style:table-column-properties style:column-width="4.002cm" style:rel-column-width="2269*"/>
</style:style>
<style:style style:name="Lines.1" style:family="table-row">
<style:table-row-properties fo:keep-together="always"/>
</style:style>
<style:style style:name="Lines.A1" style:family="table-cell">
<style:table-cell-properties fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Lines.A2" style:family="table-cell">
<style:table-cell-properties fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Lines.A3" style:family="table-cell">
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Lines.G3" style:family="table-cell">
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Lines.A4" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="0.05pt solid #000000" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Lines.C4" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Lines.G4" style:family="table-cell">
<style:table-cell-properties fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Lines.A5" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.A6" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.B6" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.C6" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.D6" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.E6" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.F6" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.G6" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.A7" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000"/>
</style:style>
<style:style style:name="Lines.A8" style:family="table-cell">
<style:table-cell-properties fo:background-color="#e6e6e6" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table3" style:family="table" style:master-page-name="">
<style:table-properties style:width="23.94cm" style:page-number="auto" fo:break-before="auto" fo:break-after="auto" table:align="margins" fo:background-color="transparent" style:shadow="none" fo:keep-with-next="always" style:may-break-between-rows="true" style:writing-mode="lr-tb">
<style:background-image/>
</style:table-properties>
</style:style>
<style:style style:name="Table3.A" style:family="table-column">
<style:table-column-properties style:column-width="5.851cm" style:rel-column-width="3317*"/>
</style:style>
<style:style style:name="Table3.B" style:family="table-column">
<style:table-column-properties style:column-width="3.362cm" style:rel-column-width="1906*"/>
</style:style>
<style:style style:name="Table3.E" style:family="table-column">
<style:table-column-properties style:column-width="4.001cm" style:rel-column-width="2268*"/>
</style:style>
<style:style style:name="Table3.1" style:family="table-row">
<style:table-row-properties fo:background-color="transparent" fo:keep-together="always">
<style:background-image/>
</style:table-row-properties>
</style:style>
<style:style style:name="Table3.A1" style:family="table-cell">
<style:table-cell-properties style:vertical-align="" fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border="0.05pt solid #000000" style:writing-mode="lr-tb">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table3.A2" style:family="table-cell">
<style:table-cell-properties style:vertical-align="" fo:background-color="#b3b3b3" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table3.A3" style:family="table-cell">
<style:table-cell-properties style:vertical-align="" fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="none" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="Table3.F3" style:family="table-cell">
<style:table-cell-properties style:vertical-align="" fo:background-color="#cccccc" fo:padding="0.097cm" fo:border-left="0.05pt solid #000000" fo:border-right="0.05pt solid #000000" fo:border-top="none" fo:border-bottom="0.05pt solid #000000" style:writing-mode="lr-tb">
<style:background-image/>
</style:table-cell-properties>
</style:style>
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Footer">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
</style:style>
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:keep-together="always" style:shadow="none" fo:keep-with-next="always"/>
</style:style>
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:keep-together="always" fo:keep-with-next="always"/>
</style:style>
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" style:shadow="none" fo:keep-with-next="always"/>
<style:text-properties fo:font-size="12pt" fo:font-weight="bold" style:font-size-asian="12pt" style:font-weight-asian="bold" style:font-size-complex="12pt" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P8" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-size="12pt" style:text-underline-style="none" fo:font-weight="normal" style:font-size-asian="10.5pt" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="P9" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="P10" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="P11" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties officeooo:paragraph-rsid="002aca3c" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="P12" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
</style:style>
<style:style style:name="P13" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties fo:font-size="11pt" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
</style:style>
<style:style style:name="P14" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties fo:font-size="11pt" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
</style:style>
<style:style style:name="P15" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties fo:font-size="11pt" style:text-underline-style="none" style:font-size-asian="11pt" style:font-size-complex="11pt"/>
</style:style>
<style:style style:name="P16" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties style:text-underline-style="none"/>
</style:style>
<style:style style:name="P17" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="00251ac8"/>
</style:style>
<style:style style:name="P18" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="none"/>
</style:style>
<style:style style:name="P19" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="00251ac8" fo:background-color="transparent"/>
</style:style>
<style:style style:name="P20" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:text-properties style:text-underline-style="none"/>
</style:style>
<style:style style:name="P21" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:break-before="page"/>
<style:text-properties style:text-underline-style="none"/>
</style:style>
<style:style style:name="P22" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:text-properties officeooo:paragraph-rsid="0037c304"/>
</style:style>
<style:style style:name="P23" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:text-properties officeooo:paragraph-rsid="003a045d"/>
</style:style>
<style:style style:name="P24" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:text-properties officeooo:rsid="003955ec" officeooo:paragraph-rsid="003a045d"/>
</style:style>
<style:style style:name="P25" style:family="paragraph" style:parent-style-name="Heading_20_1">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="P26" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:font-name="Liberation Serif1" fo:font-weight="bold" style:font-size-asian="10.5pt" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P27" style:family="paragraph" style:parent-style-name="Header">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
</style:style>
<style:style style:name="P28" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
</style:style>
<style:style style:name="P29" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:color="#000000" loext:opacity="100%" style:text-outline="false" style:text-line-through-style="none" style:text-line-through-type="none" style:font-name="Liberation Serif" fo:font-size="12pt" fo:font-style="normal" fo:text-shadow="none" style:text-underline-style="none" fo:font-weight="normal" style:font-size-asian="12pt" style:font-style-asian="normal" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-style-complex="normal" style:font-weight-complex="normal" style:text-overline-style="none" style:text-overline-color="font-color"/>
</style:style>
<style:style style:name="P30" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties officeooo:paragraph-rsid="002aca3c" style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="P31" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties style:font-size-asian="10.5pt"/>
</style:style>
<style:style style:name="P32" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="end" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P33" style:family="paragraph" style:parent-style-name="Table_20_Contents">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/>
</style:style>
<style:style style:name="P34" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P35" style:family="paragraph" style:parent-style-name="Table_20_Heading">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false"/>
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
</style:style>
<style:style style:name="P36" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:break-before="page" fo:keep-with-next="always"/>
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
</style:style>
<style:style style:name="P37" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties style:text-underline-style="none" officeooo:paragraph-rsid="003e177a"/>
</style:style>
<style:style style:name="P38" style:family="paragraph" style:parent-style-name="Text_20_body">
<style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:keep-together="always" fo:keep-with-next="always"/>
<style:text-properties fo:font-size="12pt" style:text-underline-style="none" fo:font-weight="normal" officeooo:paragraph-rsid="003e177a" fo:background-color="transparent" style:font-size-asian="10.5pt" style:font-weight-asian="normal" style:font-size-complex="12pt" style:font-weight-complex="normal"/>
</style:style>
<style:style style:name="T1" style:family="text">
<style:text-properties officeooo:rsid="0037c304"/>
</style:style>
<style:style style:name="T2" style:family="text">
<style:text-properties officeooo:rsid="0039b016"/>
</style:style>
<style:style style:name="T3" style:family="text">
<style:text-properties officeooo:rsid="003e177a"/>
</style:style>
<style:style style:name="T4" style:family="text">
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
</style:style>
<style:style style:name="T5" style:family="text">
<style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" officeooo:rsid="003e177a"/>
</style:style>
<style:style style:name="T6" style:family="text">
<style:text-properties style:text-underline-style="none"/>
</style:style>
<style:style style:name="T7" style:family="text">
<style:text-properties style:text-underline-style="none" officeooo:rsid="003e177a"/>
</style:style>
<style:page-layout style:name="pm1">
<style:page-layout-properties fo:page-width="27.94cm" fo:page-height="21.59cm" style:num-format="1" style:print-orientation="landscape" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="44" style:layout-grid-base-height="0.55cm" style:layout-grid-ruby-height="0cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="true" style:layout-grid-display="true" style:footnote-max-height="0cm" loext:margin-gutter="0cm">
<style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="none" style:adjustment="left" style:rel-width="25%" style:color="#000000"/>
</style:page-layout-properties>
<style:header-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-bottom="0.499cm"/>
</style:header-style>
<style:footer-style>
<style:header-footer-properties fo:min-height="0cm" fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.499cm"/>
</style:footer-style>
</style:page-layout>
<style:style style:name="dp1" style:family="drawing-page">
<style:drawing-page-properties draw:background-size="full"/>
</style:style>
</office:automatic-styles>
<office:master-styles>
<style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1">
<style:header>
<table:table table:name="Table2" table:style-name="Table2">
<table:table-column table:style-name="Table2.A" table:number-columns-repeated="3"/>
<table:table-row table:style-name="TableLine94368948362784">
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
<text:p text:style-name="Header">Company: <text:placeholder text:placeholder-type="text">&lt;company.rec_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
<text:p text:style-name="P27">General Ledger Detail<text:placeholder text:placeholder-type="text">&lt;if test=&quot;not context.get(&apos;posted&apos;)&quot;&gt;</text:placeholder> <text:span text:style-name="T3">Draft</text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:span></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table2.A1" office:value-type="string">
<text:p text:style-name="P2">Print Date: <text:placeholder text:placeholder-type="text">&lt;format_date(datetime.date.today(), user.language)&gt;</text:placeholder><text:s/>at <text:placeholder text:placeholder-type="text">&lt;datetime.datetime.now().strftime(&apos;%H:%M:%S&apos;)&gt;</text:placeholder></text:p>
</table:table-cell>
</table:table-row>
</table:table>
</style:header>
<style:footer>
<table:table table:name="Table1" table:style-name="Table1">
<table:table-column table:style-name="Table1.A"/>
<table:table-column table:style-name="Table1.B"/>
<table:table-row table:style-name="TableLine94368949141280">
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="Footer">User: <text:placeholder text:placeholder-type="text">&lt;user.rec_name&gt;</text:placeholder></text:p>
</table:table-cell>
<table:table-cell table:style-name="Table1.A1" office:value-type="string">
<text:p text:style-name="P3"><text:page-number text:select-page="current">1</text:page-number>/<text:page-count>3</text:page-count></text:p>
</table:table-cell>
</table:table-row>
</table:table>
</style:footer>
</style:master-page>
</office:master-styles>
<office:body>
<office:text text:use-soft-page-breaks="true">
<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
<text:sequence-decls>
<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
<text:sequence-decl text:display-outline-level="0" text:name="Table"/>
<text:sequence-decl text:display-outline-level="0" text:name="Text"/>
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
</text:sequence-decls>
<text:p text:style-name="P25">General Ledger Detail<text:placeholder text:placeholder-type="text">&lt;if test=&quot;not context.get(&apos;posted&apos;)&quot;&gt;</text:placeholder> <text:span text:style-name="T3">Draft</text:span><text:span text:style-name="T3"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:span></text:p>
<text:p text:style-name="P23"><text:span text:style-name="T1">Fiscal Year: </text:span><text:placeholder text:placeholder-type="text">&lt;fiscalyear.rec_name&gt;</text:placeholder></text:p>
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;start_period or end_period&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P23"><text:span text:style-name="T1">From Period</text:span>: <text:placeholder text:placeholder-type="text">&lt;start_period.name if start_period else &apos;&apos;&gt;</text:placeholder><text:s/><text:span text:style-name="T2">To</text:span> <text:placeholder text:placeholder-type="text">&lt;end_period.name if end_period else &apos;&apos;&gt;</text:placeholder></text:p>
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;from_date or to_date&quot;&gt;</text:placeholder></text:p>
<text:p text:style-name="P24">From Date: <text:placeholder text:placeholder-type="text">&lt;format_date(from_date, user.language) if from_date else &apos;&apos;&gt;</text:placeholder><text:s/>To <text:placeholder text:placeholder-type="text">&lt;format_date(to_date, user.language) if to_date else &apos;&apos;&gt;</text:placeholder></text:p>
<text:p text:style-name="P23"><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P20"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;group in groups&quot;&gt;</text:placeholder></text:p>
<table:table table:name="Lines" table:style-name="Lines">
<table:table-column table:style-name="Lines.A"/>
<table:table-column table:style-name="Lines.B"/>
<table:table-column table:style-name="Lines.C"/>
<table:table-column table:style-name="Lines.B" table:number-columns-repeated="2"/>
<table:table-column table:style-name="Lines.F" table:number-columns-repeated="2"/>
<table:table-header-rows>
<table:table-row table:style-name="Lines.1">
<table:table-cell table:style-name="Lines.A1" table:number-columns-spanned="7" office:value-type="string">
<text:p text:style-name="P5">Account / Currency</text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
</table:table-row>
<table:table-row table:style-name="Lines.1">
<table:table-cell table:style-name="Lines.A2" table:number-columns-spanned="7" office:value-type="string">
<text:p text:style-name="P7"><text:placeholder text:placeholder-type="text">&lt;group[&apos;account&apos;]&gt;</text:placeholder><text:s/>/<text:s/><text:placeholder text:placeholder-type="text">&lt;group[&apos;currency&apos;].rec_name if group[&apos;currency&apos;] else &apos;&apos;&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
<table:covered-table-cell/>
</table:table-row>
<table:table-row table:style-name="Lines.1">
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Date</text:p></table:table-cell>
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Journal</text:p></table:table-cell>
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Entry / Doc.</text:p></table:table-cell>
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="Table_20_Heading">Debit</text:p></table:table-cell>
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Credit</text:p></table:table-cell>
<table:table-cell table:style-name="Lines.A3" office:value-type="string"><text:p text:style-name="P6">Running</text:p></table:table-cell>
<table:table-cell table:style-name="Lines.G3" office:value-type="string"><text:p text:style-name="P6">Description / Ref.</text:p></table:table-cell>
</table:table-row>
</table:table-header-rows>
<table:table-row table:style-name="Lines.1">
<table:table-cell table:style-name="Lines.A5" table:number-columns-spanned="7" office:value-type="string">
<text:p text:style-name="P16"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in group[&apos;lines&apos;]&quot;&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/>
</table:table-row>
<table:table-row table:style-name="Lines.1">
<table:table-cell table:style-name="Lines.A6" office:value-type="string"><text:p text:style-name="P33"><text:placeholder text:placeholder-type="text">&lt;format_date(line.posting_date, user.language) if line.posting_date else &apos;&apos;&gt;</text:placeholder></text:p></table:table-cell>
<table:table-cell table:style-name="Lines.B6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;line.journal.rec_name if line.journal else &apos;&apos;&gt;</text:placeholder></text:p></table:table-cell>
<table:table-cell table:style-name="Lines.C6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;line.journal_entry_number or &apos;&apos;&gt;</text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text">&lt;line.document_number or line.voucher_number or &apos;&apos;&gt;</text:placeholder></text:p></table:table-cell>
<table:table-cell table:style-name="Lines.D6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text">&lt;format_currency(line.debit_base_currency or 0, user.language, line.base_currency or company.currency)&gt;</text:placeholder></text:p></table:table-cell>
<table:table-cell table:style-name="Lines.E6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text">&lt;format_currency(line.credit_base_currency or 0, user.language, line.base_currency or company.currency)&gt;</text:placeholder></text:p></table:table-cell>
<table:table-cell table:style-name="Lines.F6" office:value-type="string"><text:p text:style-name="P32"><text:placeholder text:placeholder-type="text">&lt;format_currency(line.running_balance_base_currency or 0, user.language, line.base_currency or company.currency)&gt;</text:placeholder></text:p></table:table-cell>
<table:table-cell table:style-name="Lines.G6" office:value-type="string"><text:p text:style-name="Table_20_Contents"><text:placeholder text:placeholder-type="text">&lt;line.description or line.move_description or &apos;&apos;&gt;</text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text">&lt;line.party.rec_name if line.party else &apos;&apos;&gt;</text:placeholder><text:line-break/><text:placeholder text:placeholder-type="text">&lt;line.reference or &apos;&apos;&gt;</text:placeholder></text:p></table:table-cell>
</table:table-row>
<text:soft-page-break/>
<table:table-row table:style-name="Lines.1">
<table:table-cell table:style-name="Lines.A7" table:number-columns-spanned="7" office:value-type="string">
<text:p text:style-name="P18"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</table:table-cell>
<table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/><table:covered-table-cell/>
</table:table-row>
</table:table>
<text:p text:style-name="P8"><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:p>
</office:text>
</office:body>
</office:document>

View File

@@ -117,6 +117,7 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView):
date = fields.Date('Effective Date', required=True, states=_MOVE_STATES)
post_date = fields.Date('Post Date', readonly=True)
description = fields.Char('Description', states=_MOVE_STATES)
ext_ref = fields.Char('Ext. Ref')
origin = fields.Reference('Origin', selection='get_origin',
states=_MOVE_STATES)
state = fields.Selection([
@@ -921,6 +922,7 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
fields.Reference("Move Origin", selection='get_move_origin'),
'get_move_field', searcher='search_move_field')
description = fields.Char('Description', states=_states)
ext_ref = fields.Char('Ext. Ref')
move_description_used = fields.Function(
fields.Char("Move Description", states=_states),
'get_move_field',
@@ -1011,7 +1013,9 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
journal = fields.Function(fields.Many2One('account.journal',"Journal"),'get_journal')
rate = fields.Function(fields.Numeric("Rate",digits=(1,6)),'get_rate')
rate = fields.Numeric("Rate", digits=(10, 6), states={
'readonly': _states['readonly'],
})
del _states
@@ -1115,46 +1119,84 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
return line.move.id
def get_rate(self,name=None):
if self.amount_second_currency:
return round((self.credit if self.credit else self.debit) / abs(self.amount_second_currency),6)
@fields.depends(
'debit', 'credit',
'date','second_currency','amount_second_currency')
def on_change_amount_second_currency(self):
Currency = Pool().get('currency.currency')
Date = Pool().get('ir.date')
if self.second_currency != None and self.amount_second_currency != None and (not self.credit and not self.debit):
tdate = Date.today()
if self.date:
tdate = self.date
rate = Currency._get_rate([self.second_currency],tdate)
if rate:
rate = rate[self.second_currency.id]
if self.amount_second_currency > 0:
self.debit = round(rate * self.amount_second_currency,2)
elif self.amount_second_currency > 0:
self.credit = round(rate * abs(self.amount_second_currency),2)
self.rate = self.get_rate()
amount = self.credit if self.credit else self.debit
amount_second_currency = getattr(self, 'amount_second_currency', None)
if amount_second_currency and amount:
return round(abs(amount_second_currency) / amount, 6)
def _set_rate_from_amounts(self):
if (getattr(self, 'amount_second_currency', None)
and not getattr(self, 'rate', None)):
self.rate = self.get_rate()
@fields.depends(
'debit', 'credit',
'date','second_currency','amount_second_currency')
'date','second_currency','amount_second_currency', 'rate',
'origin', 'move_origin', 'move', '_parent_move.origin',
'company', 'account')
def on_change_second_currency(self):
if self._manual_rate_mode():
self.rate = self._get_second_currency_rate()
self._compute_amount_from_second_currency()
@fields.depends(
'debit', 'credit',
'date','second_currency','amount_second_currency', 'rate',
'origin', 'move_origin', 'move', '_parent_move.origin',
'company', 'account')
def on_change_rate(self):
if self._manual_rate_mode() and not self.debit and not self.credit:
self._compute_amount_from_second_currency()
def _manual_rate_mode(self):
if getattr(self, 'origin', None) or getattr(self, 'move_origin', None):
return False
move = getattr(self, 'move', None)
if move and getattr(move, 'origin', None):
return False
return True
def _get_second_currency_rate(self):
Currency = Pool().get('currency.currency')
Date = Pool().get('ir.date')
if self.second_currency != None and self.amount_second_currency != None and (not self.credit and not self.debit):
tdate = Date.today()
if self.date:
tdate = self.date
rate = Currency._get_rate([self.second_currency],tdate)
if rate:
rate = rate[self.second_currency.id]
if self.second_currency is None:
return None
tdate = Date.today()
if self.date:
tdate = self.date
rates = Currency._get_rate([self.second_currency], tdate)
return rates.get(self.second_currency.id) if rates else None
def _get_company_currency(self):
Company = Pool().get('company.company')
for record in (self, getattr(self, 'move', None),
getattr(self, 'account', None)):
company = getattr(record, 'company', None)
if company:
return company.currency
company_id = Transaction().context.get('company')
if company_id:
return Company(company_id).currency
def _compute_amount_from_second_currency(self):
if self.second_currency is not None and self.amount_second_currency is not None:
if not self.rate:
self.rate = self._get_second_currency_rate()
if self.rate:
company_currency = self._get_company_currency()
if not company_currency:
return
amount = company_currency.round(
abs(self.amount_second_currency) / self.rate)
if self.amount_second_currency > 0:
self.debit = round(rate * self.amount_second_currency,2)
elif self.amount_second_currency > 0:
self.credit = round(rate * abs(self.amount_second_currency),2)
self.rate = self.get_rate()
self.debit = amount
self.credit = Decimal(0)
elif self.amount_second_currency < 0:
self.debit = Decimal(0)
self.credit = amount
else:
self.debit = Decimal(0)
self.credit = Decimal(0)
@fields.depends(
'move', 'debit', 'credit',
@@ -1221,15 +1263,26 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
if self.debit:
self.credit = Decimal(0)
self._amount_second_currency_sign()
self.rate = self.get_rate()
@fields.depends('debit', 'credit', 'amount_second_currency')
def on_change_credit(self):
if self.credit:
self.debit = Decimal(0)
self._amount_second_currency_sign()
self.rate = self.get_rate()
@fields.depends('amount_second_currency', 'debit', 'credit')
@fields.depends(
'amount_second_currency', 'debit', 'credit', 'date', 'second_currency',
'rate', 'origin', 'move_origin', 'move', '_parent_move.origin',
'company', 'account')
def on_change_amount_second_currency(self):
if self._manual_rate_mode():
if not self.rate:
self.rate = self._get_second_currency_rate()
self._compute_amount_from_second_currency()
elif not self.rate:
self.rate = self.get_rate()
self._amount_second_currency_sign()
def _amount_second_currency_sign(self):
@@ -1239,6 +1292,7 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
self.amount_second_currency = \
self.amount_second_currency.copy_sign(
(self.debit or 0) - (self.credit or 0))
self._set_rate_from_amounts()
@fields.depends('account')
def on_change_account(self):
@@ -1543,6 +1597,14 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
for fname in move_fields(move_name=False):
vals.setdefault(fname, None)
lines = super(Line, cls).create(vlist)
to_save = []
for line in lines:
if (getattr(line, 'amount_second_currency', None)
and not getattr(line, 'rate', None)):
line._set_rate_from_amounts()
to_save.append(line)
if to_save:
cls.save(to_save)
period_and_journals = set((line.period, line.journal)
for line in lines)
for period, journal in period_and_journals:

View File

@@ -12,7 +12,7 @@ from trytond.modules.account.exceptions import (
from trytond.modules.account.tax import TaxableMixin
from trytond.modules.company.tests import (
CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company)
from trytond.modules.currency.tests import create_currency
from trytond.modules.currency.tests import add_currency_rate, create_currency
from trytond.pool import Pool
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.transaction import Transaction, inactive_records
@@ -576,6 +576,54 @@ class AccountTestCase(
self.assertEqual(
cash_cur.amount_second_currency, Decimal(50))
@with_transaction()
def test_move_line_second_currency_amount_on_change(self):
'Test account move line computes amount from second currency'
pool = Pool()
Account = pool.get('account.account')
Line = pool.get('account.move.line')
company = create_company()
with set_company(company):
create_chart(company)
second_currency = create_currency('sec')
add_currency_rate(second_currency, Decimal('0.939506'))
expense, = Account.search([
('type.expense', '=', True),
])
line = Line(
account=expense,
second_currency=second_currency,
amount_second_currency=Decimal('100.00'))
line.on_change_amount_second_currency()
self.assertEqual(line.debit, Decimal('106.44'))
self.assertEqual(line.credit, Decimal(0))
self.assertEqual(line.rate, Decimal('0.939506'))
self.assertEqual(line.get_rate(), Decimal('0.939496'))
line = Line(
account=expense,
credit=Decimal('7.61'),
second_currency=second_currency,
amount_second_currency=Decimal('-108.10'))
line.on_change_amount_second_currency()
self.assertEqual(line.debit, Decimal(0))
self.assertEqual(line.credit, Decimal('115.06'))
self.assertEqual(line.rate, Decimal('0.939506'))
self.assertEqual(line.get_rate(), Decimal('0.939510'))
line.debit = line.credit = Decimal(0)
line.rate = Decimal('1.100000')
line.on_change_rate()
self.assertEqual(line.debit, Decimal(0))
self.assertEqual(line.credit, Decimal('98.27'))
self.assertEqual(line.rate, Decimal('1.100000'))
@with_transaction()
def test_account_type_amount(self):
"Test account type amount"
@@ -2049,5 +2097,104 @@ class AccountTestCase(
self.assertEqual(account.parent, new_account)
@with_transaction()
def test_general_ledger_detail_models_are_registered(self):
"Test General Ledger Detail models are registered with expected fields"
pool = Pool()
Detail = pool.get('account.general_ledger.detail')
DetailContext = pool.get('account.general_ledger.detail.context')
DetailReport = pool.get(
'account.general_ledger.detail.report', type='report')
for name in [
'row_type', 'account_code', 'transaction_currency',
'journal_entry_number', 'document_number',
'debit_base_currency', 'credit_base_currency',
'running_balance_base_currency',
'debit_transaction_currency',
'credit_transaction_currency',
'running_balance_transaction_currency', 'payable_qty']:
self.assertIn(name, Detail._fields)
for name in [
'debit_base_currency', 'credit_base_currency',
'balance_base_currency', 'running_balance_base_currency',
'debit_transaction_currency',
'credit_transaction_currency',
'balance_transaction_currency',
'running_balance_transaction_currency']:
self.assertEqual(Detail._fields[name].digits, (16, 2))
self.assertEqual(Detail._fields['payable_qty'].digits, (16, 5))
for name in [
'account', 'account_from', 'account_to', 'currency', 'party',
'document_number']:
self.assertIn(name, DetailContext._fields)
account = pool.get('account.account').__table__()
account_label = Detail._account_label(account)
self.assertIn('||', str(account_label))
self.assertNotIn(' + ', str(account_label))
self.assertEqual(account_label.params, ('', ' - ', ''))
self.assertEqual(
DetailReport.__name__, 'account.general_ledger.detail.report')
@with_transaction()
def test_general_ledger_detail_report_groups_by_account_currency(self):
"Test General Ledger Detail report groups lines by account and currency"
DetailReport = Pool().get(
'account.general_ledger.detail.report', type='report')
currency = type('Currency', (), {'id': 1})()
other_currency = type('Currency', (), {'id': 2})()
first = type('Line', (), {
'account_code': '1000 - Cash',
'account_name': 'Cash',
'transaction_currency': currency,
})()
second = type('Line', (), {
'account_code': '1000 - Cash',
'account_name': 'Cash',
'transaction_currency': currency,
})()
third = type('Line', (), {
'account_code': '2000 - Revenue',
'account_name': 'Revenue',
'transaction_currency': other_currency,
})()
groups = DetailReport._groups([first, second, third])
self.assertEqual(len(groups), 2)
self.assertEqual(groups[0]['account'], '1000 - Cash')
self.assertEqual(groups[0]['currency'], currency)
self.assertEqual(groups[0]['lines'], [first, second])
self.assertEqual(groups[1]['account'], '2000 - Revenue')
self.assertEqual(groups[1]['currency'], other_currency)
self.assertEqual(groups[1]['lines'], [third])
@with_transaction()
def test_general_ledger_detail_date_contexts(self):
"Test General Ledger Detail opening and movement date contexts"
Detail = Pool().get('account.general_ledger.detail')
context = {
'from_date': datetime.date(2026, 1, 10),
'to_date': datetime.date(2026, 1, 31),
}
self.assertEqual(Detail._query_context('opening', context), {
'periods': None,
'from_date': None,
'to_date': datetime.date(2026, 1, 9),
})
self.assertEqual(Detail._query_context('movement', context), {
'periods': None,
'from_date': datetime.date(2026, 1, 10),
'to_date': datetime.date(2026, 1, 31),
})
self.assertEqual(Detail._query_context('closing', context), {
'periods': None,
'from_date': None,
'to_date': datetime.date(2026, 1, 31),
})
del ModuleTestCase

View File

@@ -0,0 +1,41 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="fiscalyear"/>
<field name="fiscalyear"/>
<label name="company"/>
<field name="company"/>
<label name="journal"/>
<field name="journal" widget="selection"/>
<group col="-1" colspan="2" id="checkboxes">
<label name="posted"/>
<field name="posted"/>
</group>
<label name="start_period"/>
<field name="start_period"/>
<label name="end_period"/>
<field name="end_period"/>
<label name="from_date"/>
<field name="from_date"/>
<label name="to_date"/>
<field name="to_date"/>
<label name="account"/>
<field name="account"/>
<label name="currency"/>
<field name="currency"/>
<label name="account_from"/>
<field name="account_from"/>
<label name="account_to"/>
<field name="account_to"/>
<label name="party"/>
<field name="party"/>
<label name="document_number"/>
<field name="document_number"/>
</form>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree>
<field name="row_type"/>
<field name="account_code" expand="1"/>
<field name="transaction_currency"/>
<field name="base_currency" optional="1"/>
<field name="posting_date"/>
<field name="journal"/>
<field name="journal_entry_number"/>
<field name="document_number"/>
<field name="voucher_number" optional="1"/>
<field name="document_type" optional="1"/>
<field name="document_date" optional="1"/>
<field name="posting_status" optional="1"/>
<field name="party" optional="1"/>
<field name="description" expand="1" optional="1"/>
<field name="move_description" expand="1" optional="1"/>
<field name="reference" optional="1"/>
<field name="origin" optional="1"/>
<field name="debit_base_currency" sum="1"/>
<field name="credit_base_currency" sum="1"/>
<field name="balance_base_currency" sum="1"/>
<field name="running_balance_base_currency" optional="1"/>
<field name="debit_transaction_currency" sum="1" optional="1"/>
<field name="credit_transaction_currency" sum="1" optional="1"/>
<field name="balance_transaction_currency" sum="1" optional="1"/>
<field name="running_balance_transaction_currency" optional="1"/>
<field name="payable_qty" sum="1" optional="1"/>
<field name="uom" optional="1"/>
<field name="entered_by" optional="1"/>
<field name="entered_date" optional="1"/>
<field name="modified_by" optional="1"/>
<field name="modified_date" optional="1"/>
<field name="move" optional="1"/>
<field name="move_line" optional="1"/>
<field name="company" optional="1"/>
</tree>

View File

@@ -21,6 +21,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="origin" colspan="3"/>
<label name="description_used"/>
<field name="description_used" colspan="3"/>
<label name="ext_ref"/>
<field name="ext_ref" colspan="3"/>
<notebook>
<page name="lines">
<field name="lines" colspan="4"

View File

@@ -26,6 +26,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="origin"/>
<label name="description_used"/>
<field name="description_used" colspan="3"/>
<label name="ext_ref"/>
<field name="ext_ref" colspan="3"/>
<notebook colspan="4">
<page string="Other Info" id="info">
<label name="date"/>

View File

@@ -26,6 +26,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="amount_second_currency" symbol=""/>
<label name="second_currency"/>
<field name="second_currency"/>
<label name="rate"/>
<field name="rate"/>
</page>
<page name="tax_lines">
<field name="tax_lines" colspan="4"/>

View File

@@ -4,6 +4,7 @@ this repository contains the full copyright notices and license terms. -->
<tree editable="1">
<field name="move"/>
<field name="account" expand="1"/>
<field name="ext_ref" expand="1" optional="1"/>
<field name="party" expand="1"/>
<field name="debit" sum="1"/>
<field name="credit" sum="1"/>

View File

@@ -0,0 +1,15 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.pool import Pool
from . import account
def register():
Pool.register(
account.AccountTemplate,
module='account_ch_os', type_='model')
Pool.register(
account.CreateChart,
module='account_ch_os', type_='wizard')

View File

@@ -0,0 +1,27 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.pool import Pool, PoolMeta
class AccountTemplate(metaclass=PoolMeta):
__name__ = 'account.account.template'
class CreateChart(metaclass=PoolMeta):
__name__ = 'account.create_chart'
def default_properties(self, fields):
pool = Pool()
ModelData = pool.get('ir.model.data')
defaults = super().default_properties(fields)
try:
template_id = ModelData.get_id('account_ch_os', 'root')
except KeyError:
return defaults
if self.account.account_template.id == template_id:
defaults['account_receivable'] = self.get_account(
'account_ch_os.acct_110000')
defaults['account_payable'] = self.get_account(
'account_ch_os.acct_200000')
return defaults

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

View File

@@ -0,0 +1,12 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.tests.test_tryton import ModuleTestCase
class AccountCHOSTestCase(ModuleTestCase):
'Test Account CH OS module'
module = 'account_ch_os'
del ModuleTestCase

View File

@@ -0,0 +1,6 @@
[tryton]
version=7.2.3
depends:
account
xml:
account_ch_os.xml

File diff suppressed because it is too large Load Diff

View File

@@ -485,7 +485,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
})
cls.__rpc__.update({
'post': RPC(
readonly=False, instantiate=0, fresh_session=True),
readonly=False, instantiate=0, fresh_session=False),
})
@classmethod
@@ -515,7 +515,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
~((table.state == 'cancelled') & (table.number == Null)),
CharLength(table.number), table.number]
@fields.depends('selection_rate','rate_date')
@fields.depends(
'selection_rate', 'rate_date', 'currency', 'company', 'invoice_date',
'lines')
def on_change_with_rate(self, name=None):
return self.get_selected_rate()
@@ -528,8 +530,8 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
def get_selected_rate(self,name=None):
Currency = Pool().get('currency.currency')
Date = Pool().get('ir.date')
company = self.company
currency = self.currency
company = getattr(self, 'company', None)
currency = getattr(self, 'currency', None)
if not currency or not company:
return None
@@ -563,6 +565,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
if rate:
return round(1/rate,6) or 1
def _compute_company_currency_amount(self, amount, date=None):
Currency = Pool().get('currency.currency')
if self.currency == self.company.currency:
return amount
if self.rate:
return self.company.currency.round(amount / self.rate)
with Transaction().set_context(date=date or self.currency_date):
return Currency.compute(
self.currency, amount, self.company.currency)
@staticmethod
def default_type():
return Transaction().context.get('type', 'out')
@@ -1218,6 +1230,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
line.amount_second_currency = (
line.amount_second_currency.copy_sign(
line.debit - line.credit))
line.rate = line.get_rate()
line.account = self.account
logger.info("_GET_MOVE_LINE_ACCOUNT:%s",line.account)
if self.account.party_required:
@@ -1286,9 +1299,16 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
remainder = sum(l.debit - l.credit for l in move_lines)
if self.payment_term:
payment_date = self.payment_term_date or self.invoice_date or today
purchase_line = int(str(self.lines[0].origin).split(",")[1]) if self.lines[0].origin else None
origin = self.lines[0].origin if self.lines and self.lines[0].origin else None
model = str(origin).split(",")[0] if origin else None
logger.info("MODEL:%s",model)
line = None
if model:
Line = Pool().get(model)
line = Line(int(str(origin).split(",")[1]))
logger.info("LINE:%s",line)
term_lines = self.payment_term.compute(
self.total_amount, self.currency, payment_date, purchase_line)
self.total_amount, self.currency, payment_date, line)
else:
term_lines = [(self.payment_term_date or today, self.total_amount)]
past_payment_term_dates = []
@@ -1885,17 +1905,17 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
cls._check_taxes(invoices)
# cls._check_similar(invoices)
invoices_in = cls.browse([i for i in invoices if i.type == 'in'])
cls.set_number(invoices_in)
cls.set_number(invoices)
cls._store_cache(invoices)
moves = []
for invoice in invoices:
if invoice.type == 'in':
move = invoice.get_move()
if move != invoice.move:
invoice.move = move
moves.append(move)
if invoice.currency != invoice.company.currency and not invoice.rate:
invoice.rate = invoice.get_selected_rate()
move = invoice.get_move()
if move != invoice.move:
invoice.move = move
moves.append(move)
invoice.do_lot_invoicing()
if moves:
Move.save(moves)
@@ -1958,21 +1978,35 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
move_line_.amount_second_currency = -original_amount
if amount < 0:
lot_has_sale_invoice = (
move_line.lot
and (move_line.lot.sale_invoice_line
or move_line.lot.sale_invoice_line_prov))
move_line.debit = Decimal(0)
move_line.credit = -amount
move_line.account = gl.product.account_stock_used
move_line.account = gl.product.account_stock_used if not lot_has_sale_invoice else gl.product.account_stock_out_used
move_line.account = gl.product.account_cogs_used if gl.fee else move_line.account
move_line_.credit = Decimal(0)
move_line_.debit = -amount
move_line_.account = gl.product.account_stock_in_used
else:
lot_has_sale_invoice = (
move_line.lot
and (move_line.lot.sale_invoice_line
or move_line.lot.sale_invoice_line_prov))
move_line.debit = amount
move_line.credit = Decimal(0)
move_line.account = gl.product.account_stock_used
move_line.account = gl.product.account_stock_used if not lot_has_sale_invoice else gl.product.account_stock_out_used
move_line.account = gl.product.account_cogs_used if gl.fee else move_line.account
move_line_.debit = Decimal(0)
move_line_.credit = amount
move_line_.account = gl.product.account_stock_in_used
move_lines = [move_line,move_line_]
if getattr(move_line, 'amount_second_currency', None):
move_line.rate = move_line.get_rate()
if getattr(move_line_, 'amount_second_currency', None):
move_line_.rate = move_line_.get_rate()
if drop:
drop_line = AccountMoveLine()
@@ -2010,6 +2044,10 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
drop_line_.second_currency = self.currency
drop_line_.amount_second_currency = -original_amount
move_lines = [drop_line,drop_line_]
if getattr(drop_line, 'amount_second_currency', None):
drop_line.rate = drop_line.get_rate()
if getattr(drop_line_, 'amount_second_currency', None):
drop_line_.rate = drop_line_.get_rate()
return move_lines
@@ -2031,7 +2069,17 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin, InvoiceReportMixin):
var_qt = sum([i.quantity for i in gl])
logger.info("LOT_TO_PROCESS:%s",lot)
logger.info("FEE_TO_PROCESS:%s",gl[0].fee)
if lot:
fee_correction = (
gl[0].fee
and any(Decimal(str(i.quantity or 0)) < 0 for i in gl)
and any(Decimal(str(i.quantity or 0)) > 0 for i in gl))
if (gl[0].fee and not gl[0].product.landed_cost
and not fee_correction):
diff = gl[0].fee.amount - gl[0].fee.get_non_cog(lot)
account_move = gl[0].fee._get_account_move_fee(lot,'in',diff)
if account_move:
Move.save([account_move])
if (lot and not gl[0].fee) or (gl[0].fee and gl[0].product.landed_cost):
adjust_move_lines = []
mov = None
if self.type == 'in':
@@ -3163,7 +3211,6 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
def _compute_taxes(self):
pool = Pool()
Currency = pool.get('currency.currency')
TaxLine = pool.get('account.tax.line')
tax_lines = []
@@ -3172,11 +3219,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
taxes = self._get_taxes().values()
for tax in taxes:
amount = tax['base']
with Transaction().set_context(
date=self.invoice.currency_date):
amount = Currency.compute(
self.invoice.currency, amount,
self.invoice.company.currency)
amount = self.invoice._compute_company_currency_amount(amount)
tax_line = TaxLine()
tax_line.amount = amount
tax_line.type = 'base'
@@ -3241,6 +3284,7 @@ class InvoiceLine(sequence_ordered(), ModelSQL, ModelView, TaxableMixin):
line.amount_second_currency = (
line.amount_second_currency.copy_sign(
line.debit - line.credit))
line.rate = line.get_rate()
line.account = self.account
logger.info("GET_MOVE_LINES_ACCOUNT:%s",line.account)
if self.account.party_required:
@@ -3500,9 +3544,7 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
'''
Return a list of move lines instances for invoice tax
'''
Currency = Pool().get('currency.currency')
pool = Pool()
Currency = pool.get('currency.currency')
MoveLine = pool.get('account.move.line')
TaxLine = pool.get('account.tax.line')
line = MoveLine()
@@ -3510,11 +3552,8 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
return []
line.description = self.description
if self.invoice.currency != self.invoice.company.currency:
with Transaction().set_context(date=self.invoice.currency_date):
amount = Currency.compute(self.invoice.currency, self.amount,
self.invoice.company.currency)
base = Currency.compute(self.invoice.currency, self.base,
self.invoice.company.currency)
amount = self.invoice._compute_company_currency_amount(self.amount)
base = self.invoice._compute_company_currency_amount(self.base)
line.amount_second_currency = self.amount
line.second_currency = self.invoice.currency
else:
@@ -3536,6 +3575,7 @@ class InvoiceTax(sequence_ordered(), ModelSQL, ModelView):
line.amount_second_currency = (
line.amount_second_currency.copy_sign(
line.debit - line.credit))
line.rate = line.get_rate()
line.account = self.account
if self.account.party_required:
line.party = self.invoice.party
@@ -3684,13 +3724,19 @@ class InvoiceReport(Report):
Invoice = pool.get('account.invoice')
# Re-instantiate because records are TranslateModel
invoice, = Invoice.browse(records)
if invoice.invoice_report_cache:
report_path = cls._get_action_report_path(action)
use_cache = (
report_path in (None, 'account_invoice/invoice.fodt')
and invoice.invoice_report_cache
)
if use_cache:
return (
invoice.invoice_report_format,
invoice.invoice_report_cache)
else:
result = super()._execute(records, header, data, action)
if invoice.invoice_report_versioned:
if (invoice.invoice_report_versioned
and report_path in (None, 'account_invoice/invoice.fodt')):
format_, data = result
if isinstance(data, str):
data = bytes(data, 'utf-8')
@@ -3707,6 +3753,12 @@ class InvoiceReport(Report):
with Transaction().set_context(language=False):
return super().render(*args, **kwargs)
@staticmethod
def _get_action_report_path(action):
if isinstance(action, dict):
return action.get('report')
return getattr(action, 'report', None)
@classmethod
def execute(cls, ids, data):
pool = Pool()

View File

@@ -264,11 +264,6 @@ this repository contains the full copyright notices and license terms. -->
<field name="wiz_name">account.invoice.refresh_invoice_report</field>
<field name="model">account.invoice</field>
</record>
<record model="ir.action.keyword" id="refresh_invoice_report_keyword">
<field name="keyword">form_print</field>
<field name="model">account.invoice,-1</field>
<field name="action" ref="refresh_invoice_report_wizard"/>
</record>
<record model="ir.action-res.group" id="refresh_invoice_report-group_account_admin">
<field name="action" ref="refresh_invoice_report_wizard"/>
<field name="group" ref="account.group_account_admin"/>
@@ -318,6 +313,58 @@ this repository contains the full copyright notices and license terms. -->
<field name="action" ref="report_prepayment"/>
</record>
<record model="ir.action.report" id="report_invoice_ict_final">
<field name="name">CN/DN</field>
<field name="model">account.invoice</field>
<field name="report_name">account.invoice</field>
<field name="report">account_invoice/invoice_ict_final.fodt</field>
<field name="single" eval="True"/>
</record>
<record model="ir.action.keyword" id="report_invoice_ict_final_keyword">
<field name="keyword">form_print</field>
<field name="model">account.invoice,-1</field>
<field name="action" ref="report_invoice_ict_final"/>
</record>
<record model="ir.action.report" id="report_invoice_ict_commission_final">
<field name="name">CN/DN Commission</field>
<field name="model">account.invoice</field>
<field name="report_name">account.invoice</field>
<field name="report">account_invoice/commission__ict_final.fodt</field>
<field name="single" eval="True"/>
</record>
<record model="ir.action.keyword" id="report_invoice_ict_commission_final_keyword">
<field name="keyword">form_print</field>
<field name="model">account.invoice,-1</field>
<field name="action" ref="report_invoice_ict_commission_final"/>
</record>
<record model="ir.action.report" id="report_sale_commission_ict">
<field name="name">Commission invoice Sale</field>
<field name="model">account.invoice</field>
<field name="report_name">account.invoice</field>
<field name="report">account_invoice/sale_commission_ict.fodt</field>
<field name="single" eval="True"/>
</record>
<record model="ir.action.keyword" id="report_sale_commission_ict_keyword">
<field name="keyword">form_print</field>
<field name="model">account.invoice,-1</field>
<field name="action" ref="report_sale_commission_ict"/>
</record>
<record model="ir.action.report" id="report_purchase_commission_ict">
<field name="name">Commission invoice Purchase</field>
<field name="model">account.invoice</field>
<field name="report_name">account.invoice</field>
<field name="report">account_invoice/purchase_commission_ict.fodt</field>
<field name="single" eval="True"/>
</record>
<record model="ir.action.keyword" id="report_purchase_commission_ict_keyword">
<field name="keyword">form_print</field>
<field name="model">account.invoice,-1</field>
<field name="action" ref="report_purchase_commission_ict"/>
</record>
<record model="ir.sequence.type" id="sequence_type_account_invoice">
<field name="name">Invoice</field>
</record>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,10 @@ from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.wizard import Button, StateView, Wizard
from .exceptions import PaymentTermComputeError, PaymentTermValidationError
import logging
logger = logging.getLogger(__name__)
class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
@@ -46,7 +48,7 @@ class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
'.msg_payment_term_missing_last_remainder',
payment_term=term.rec_name))
def compute(self, amount, currency, date, purchase_line = None):
def compute(self, amount, currency, date, line_ = None):
"""Calculate payment terms and return a list of tuples
with (date, amount) for each payment term line.
@@ -59,7 +61,7 @@ class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
remainder = amount
for line in self.lines:
value = line.get_value(remainder, amount, currency)
value_date = line.get_date(date, purchase_line)
value_date = line.get_date(date, line_)
if value is None or not value_date:
continue
if ((remainder - value) * sign) < Decimal(0):
@@ -155,12 +157,11 @@ class PaymentTermLine(sequence_ordered(), ModelSQL, ModelView):
self.ratio = self.round(1 / self.divisor,
self.__class__.ratio.digits[1])
def get_date(self, date, purchase_line = None):
def get_date(self, date, line = None):
#find date based on trigger:
if purchase_line and self.trigger_event:
PurchaseLine = Pool().get('purchase.line')
purchase_line = PurchaseLine(purchase_line)
trigger_date = purchase_line.get_date(self.trigger_event)
if line and self.trigger_event:
trigger_date = line.get_date(self.trigger_event)
logger.info("DATE_FROM_LINE:%s",trigger_date)
if trigger_date:
date = trigger_date

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
import datetime
from decimal import Decimal
from unittest.mock import Mock, patch
from trytond.modules.account_invoice.exceptions import (
PaymentTermValidationError)
@@ -251,5 +252,70 @@ class AccountInvoiceTestCase(
(datetime.date(2012, 1, 14), Decimal('-1.0')),
])
def test_post_rpc_does_not_require_fresh_session(self):
'posting invoices does not force a fresh session'
Invoice = Pool().get('account.invoice')
self.assertFalse(Invoice.__rpc__['post'].fresh_session)
@with_transaction()
def test_validate_invoice_creates_move_for_customer_invoice(self):
'validating customer invoices now creates the account move'
Invoice = Pool().get('account.invoice')
move = Mock()
invoice = Invoice()
invoice.type = 'out'
invoice.move = None
invoice.get_move = Mock(return_value=move)
invoice.do_lot_invoicing = Mock()
move_model = Mock()
with patch.object(Invoice, '_check_taxes'), patch.object(
Invoice, '_store_cache'), patch.object(
Invoice, 'browse', return_value=[]), patch.object(
Invoice, 'cleanMoves') as clean_moves, patch.object(
Invoice, 'save') as save_invoices, patch(
'trytond.modules.account_invoice.invoice.Pool'
) as PoolMock:
PoolMock.return_value.get.return_value = move_model
Invoice.validate_invoice([invoice])
self.assertIs(invoice.move, move)
invoice.get_move.assert_called_once_with()
invoice.do_lot_invoicing.assert_called_once_with()
move_model.save.assert_called_once_with([move])
clean_moves.assert_called_once_with([move])
save_invoices.assert_called()
@with_transaction()
def test_validate_invoice_sets_number_for_customer_invoice(self):
'validating customer invoices now assigns the invoice number'
Invoice = Pool().get('account.invoice')
move = Mock()
invoice = Invoice()
invoice.type = 'out'
invoice.move = None
invoice.get_move = Mock(return_value=move)
invoice.do_lot_invoicing = Mock()
move_model = Mock()
with patch.object(Invoice, '_check_taxes'), patch.object(
Invoice, '_store_cache'), patch.object(
Invoice, 'set_number') as set_number, patch.object(
Invoice, 'cleanMoves'), patch.object(
Invoice, 'save'), patch(
'trytond.modules.account_invoice.invoice.Pool'
) as PoolMock:
PoolMock.return_value.get.return_value = move_model
Invoice.validate_invoice([invoice])
set_number.assert_called_once_with([invoice])
del ModuleTestCase

View File

@@ -92,8 +92,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="invoice_report_revisions" colspan="4"/>
</page>
<page string="Rate management" id="rate">
<label name="warning"/>
<field name="warning"/>
<!-- <label name="warning"/>
<field name="warning"/> -->
<newline/>
<label name="rate"/>
<field name="rate"/>

View File

@@ -0,0 +1,14 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.pool import Pool
from . import account
def register():
Pool.register(
account.AccountTemplate,
module='account_itsa', type_='model')
Pool.register(
account.CreateChart,
module='account_itsa', type_='wizard')

View File

@@ -0,0 +1,40 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import csv
from io import BytesIO, TextIOWrapper
from sql import Table
from sql.aggregate import Sum
from sql.conditionals import Coalesce
from trytond.config import config
from trytond.model import ModelStorage, ModelView, fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.wizard import Button, StateTransition, StateView, Wizard
class AccountTemplate(metaclass=PoolMeta):
__name__ = 'account.account.template'
@classmethod
def __register__(cls, module_name):
cursor = Transaction().connection.cursor()
model_data = Table('ir_model_data')
super().__register__(module_name)
class CreateChart(metaclass=PoolMeta):
__name__ = 'account.create_chart'
def default_properties(self, fields):
pool = Pool()
ModelData = pool.get('ir.model.data')
defaults = super().default_properties(fields)
# template_id = ModelData.get_id('account_ch.root')
# if self.account.account_template.id == template_id:
# defaults['account_receivable'] = self.get_account(
# 'account_ch.3400')
# defaults['account_payable'] = self.get_account(
# 'account_ch.6040')
return defaults

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
[tryton]
version=7.2.3
depends:
account
extras_depend:
account_invoice
xml:
account_itsa.xml
#tax_ict.xml

View File

@@ -6,7 +6,7 @@ from decimal import Decimal
from trytond.i18n import gettext
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
from trytond.exceptions import UserWarning, UserError
from .exceptions import COGSWarning
import logging
@@ -74,8 +74,8 @@ class InvoiceLine(metaclass=PoolMeta):
if move_line.second_currency:
move_line.amount_second_currency = amount
else:
move_line.debit = Decimal(0)
move_line.credit = -amount_converted
move_line.debit = -amount_converted
move_line.credit = Decimal(0)
move_line.account = self.product.account_stock_out_used
if move_line.second_currency:
move_line.amount_second_currency = amount
@@ -171,10 +171,32 @@ class InvoiceLine(metaclass=PoolMeta):
cost = self.amount
else:
cost = self.lot.get_cog()
if not cost or cost == 0:
raise UserError('No COG for this invoice, please generate the reception of the goods')
if self.amount < 0 :
cost *= -1
logger.info("GETMOVELINES_COST:%s",cost)
anglo_saxon_move_lines_ = []
with Transaction().set_context(
company=self.invoice.company.id, date=accounting_date):
anglo_saxon_move_lines = self._get_anglo_saxon_move_lines(
cost, type_)
lot_has_sale_invoice = (
self.lot
and (self.lot.sale_invoice_line_prov
or self.lot.sale_invoice_line))
if type_ == 'in_supplier' and lot_has_sale_invoice and not self.fee:
anglo_saxon_move_lines_ = self._get_anglo_saxon_move_lines(cost, 'out_customer')
result.extend(anglo_saxon_move_lines)
result.extend(anglo_saxon_move_lines_)
#Fee inventoried delivery management
if self.lot and type_ != 'in_supplier':
FeeLots = Pool().get('fee.lots')
fees = FeeLots.search(['lot','=',self.lot.id])
for fl in fees:
if fl.fee.type == 'ordered' and fl.fee.product.template.landed_cost:
AccountMove = Pool().get('account.move')
account_move = fl.fee._get_account_move_fee(fl.lot,'out')
AccountMove.save([account_move])
return result

View File

@@ -16,11 +16,11 @@ account_names = [
class Category(metaclass=PoolMeta):
__name__ = 'product.category'
account_stock = fields.MultiValue(fields.Many2One(
'account.account', "Account Stock",
'account.account', "Account Stock/Cost Income",
domain=[
('closed', '!=', True),
('type.stock', '=', True),
('type.statement', '=', 'balance'),
# ('type.stock', '=', True),
# ('type.statement', '=', 'balance'),
('company', '=', Eval('context', {}).get('company', -1)),
],
states={
@@ -29,7 +29,7 @@ class Category(metaclass=PoolMeta):
| ~Eval('accounting', False)),
}))
account_stock_in = fields.MultiValue(fields.Many2One(
'account.account', "Account Stock IN",
'account.account', "Account Stock IN/Cost liability",
domain=[
('closed', '!=', True),
('type.stock', '=', True),
@@ -41,7 +41,7 @@ class Category(metaclass=PoolMeta):
| ~Eval('accounting', False)),
}))
account_stock_out = fields.MultiValue(fields.Many2One(
'account.account', "Account Stock OUT",
'account.account', "Account Stock OUT/Cost liability",
domain=[
('closed', '!=', True),
('type.stock', '=', True),
@@ -103,8 +103,8 @@ class CategoryAccount(metaclass=PoolMeta):
'account.account', "Account Stock",
domain=[
('closed', '!=', True),
('type.stock', '=', True),
('type.statement', '=', 'balance'),
# ('type.stock', '=', True),
# ('type.statement', '=', 'balance'),
('company', '=', Eval('company', -1)),
])
account_stock_in = fields.Many2One(

View File

@@ -2,8 +2,8 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form/notebook" position="inside">
<page string="Analytic" id="analytic_accounts">
<xpath expr="/form/notebook/page[@id='invoice']" position="before">
<page string="Analytics" id="analytic_accounts">
<field name="analytic_accounts" colspan="4"/>
</page>
</xpath>

View File

@@ -1,8 +1,11 @@
from trytond.pool import Pool
from . import automation,rules #, document
from . import automation,rules,freight_booking,cron #, document
def register():
Pool.register(
automation.AutomationDocument,
rules.AutomationRuleSet,
freight_booking.FreightBookingInfo,
cron.Cron,
cron.AutomationCron,
module='automation', type_='model')

View File

@@ -1,10 +1,15 @@
from trytond.model import ModelSQL, ModelView, fields, Workflow
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.wizard import Button
from trytond.transaction import Transaction
from sql import Table
from decimal import getcontext, Decimal, ROUND_HALF_UP
import requests
import io
import logging
import json
import re
logger = logging.getLogger(__name__)
@@ -17,6 +22,7 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
('invoice', 'Invoice'),
('statement_of_facts', 'Statement of Facts'),
('weight_report', 'Weight Report'),
('controller', 'Controller'),
('bol', 'Bill of Lading'),
('controller_invoice', 'Controller Invoice'),
], 'Type')
@@ -57,25 +63,53 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
def run_ocr(cls, docs):
for doc in docs:
try:
# Décoder le fichier depuis le champ Binary
file_data = doc.document.data or b""
logger.info(f"File size: {len(file_data)} bytes")
logger.info(f"First 20 bytes: {file_data[:20]}")
logger.info(f"Last 20 bytes: {file_data[-20:]}")
if doc.type == 'weight_report':
# Décoder le fichier depuis le champ Binary
file_data = doc.document.data or b""
logger.info(f"File size: {len(file_data)} bytes")
logger.info(f"First 20 bytes: {file_data[:20]}")
logger.info(f"Last 20 bytes: {file_data[-20:]}")
file_name = doc.document.name or "document"
file_name = doc.document.name or "document"
# Envoyer le fichier au service OCR
response = requests.post(
"http://automation-service:8006/ocr",
files={"file": (file_name, io.BytesIO(file_data))}
)
response.raise_for_status()
data = response.json()
logger.info("RUN_OCR_RESPONSE:%s",data)
doc.ocr_text = data.get("ocr_text", "")
doc.state = "ocr_done"
doc.notes = (doc.notes or "") + "OCR done\n"
# Envoyer le fichier au service OCR
response = requests.post(
"http://automation-service:8006/ocr",
files={"file": (file_name, io.BytesIO(file_data))}
)
response.raise_for_status()
data = response.json()
logger.info("RUN_OCR_RESPONSE:%s",data)
doc.ocr_text = data.get("ocr_text", "")
doc.state = "ocr_done"
doc.notes = (doc.notes or "") + "OCR done\n"
else:
doc.ocr_text = (doc.document.data or b"").decode('utf-8', errors='replace')
match = re.search(r"\bID\s*:\s*(\d+)", doc.ocr_text)
if match:
request_id = match.group(1)
match = re.search(r"\bBL\s*number\s*:\s*([A-Za-z0-9_-]+)", doc.ocr_text, re.IGNORECASE)
if match:
bl_number = match.group(1)
ShipmentIn = Pool().get('stock.shipment.in')
sh = ShipmentIn.search(['bl_number','=',bl_number])
if sh:
sh[0].returned_id = request_id
ShipmentIn.save(sh)
doc.notes = (doc.notes or "") + "Id returned: " + request_id
so_payload = {
"ServiceOrderKey": sh[0].service_order_key,
"ID_Number": request_id
}
response = requests.post(
"http://automation-service:8006/service-order-update",
json=so_payload,
timeout=10
)
response.raise_for_status()
doc.notes = (doc.notes or "") + " SO updated"
except Exception as e:
doc.state = "error"
@@ -154,7 +188,8 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
logger.info("Sending OCR text to metadata API: %s", doc.ocr_text)
response = requests.post(
"http://automation-service:8006/metadata",
#"http://automation-service:8006/metadata",
"http://automation-service:8006/parse",
json={"text": doc.ocr_text or ""}
)
response.raise_for_status()
@@ -176,6 +211,18 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
logger.error("Metadata processing error: %s", e)
doc.save()
def create_weight_report(self,wr_payload):
response = requests.post(
"http://automation-service:8006/weight-report",
json=wr_payload, # 👈 ICI la correction
timeout=10
)
response.raise_for_status()
return response.json()
# -------------------------------------------------------
# FULL PIPELINE
# -------------------------------------------------------
@@ -184,18 +231,47 @@ class AutomationDocument(ModelSQL, ModelView, Workflow):
def run_pipeline(cls, docs):
for doc in docs:
try:
if cls.rule_set.ocr_required:
cls.run_ocr([doc])
if cls.rule_set.structure_required and doc.state != "error":
cls.run_structure([doc])
if cls.rule_set.table_required and doc.state != "error":
cls.run_tables([doc])
if cls.rule_set.metadata_required and doc.state != "error":
cls.run_metadata([doc])
if doc.state != "error":
doc.state = "validated"
doc.notes = (doc.notes or "") + "Pipeline completed\n"
logger.info("DATA_TYPE:%s",type(doc.metadata_json))
metadata = json.loads(str(doc.metadata_json))
logger.info("JSON STRUCTURE:%s",metadata)
WeightReport = Pool().get('weight.report')
wr = WeightReport.create_from_json(metadata)
ShipmentIn = Pool().get('stock.shipment.in')
ShipmentWR = Pool().get('shipment.wr')
sh = ShipmentIn.search([('bl_number','ilike',wr.bl_no)])
if sh:
swr = ShipmentWR()
swr.shipment_in = sh[0]
swr.wr = wr
ShipmentWR.save([swr])
doc.notes = (doc.notes or "") + f"Shipment found: {sh[0].number}\n"
logger.info("BL_NUMBER:%s",sh[0].bl_number)
doc.notes = (
(doc.notes or "")
+ "Global WR linked to shipment. "
+ "Create remote lot WRs from the weight report form.\n")
# if cls.rule_set.ocr_required:[]
# cls.run_ocr([doc])
# if cls.rule_set.structure_required and doc.state != "error":
# cls.run_structure([doc])
# if cls.rule_set.table_required and doc.state != "error":
# cls.run_tables([doc])
# if cls.rule_set.metadata_required and doc.state != "error":
# cls.run_metadata([doc])
# if doc.state != "error":
# doc.state = "validated"
# doc.notes = (doc.notes or "") + "Pipeline completed\n"
except Exception as e:
logger.exception("PIPELINE FAILED") # 👈 TRACE COMPLETE
doc.state = "error"
doc.notes = (doc.notes or "") + f"Pipeline error: {e}\n"
doc.save()
doc.save()
raise
# except Exception as e:
# doc.state = "error"
# doc.notes = (doc.notes or "") + f"Pipeline error: {e}\n"
doc.save()

View File

@@ -75,7 +75,7 @@
<record model="ir.model.button" id="auto_button1">
<field name="model">automation.document</field>
<field name="name">run_pipeline</field>
<field name="string">Run Full Pipeline</field>
<field name="string">Create Weight Report</field>
</record>
<record model="ir.model.button" id="auto_button2">
<field name="model">automation.document</field>

408
modules/automation/cron.py Normal file
View File

@@ -0,0 +1,408 @@
import requests
from decimal import getcontext, Decimal, ROUND_HALF_UP
from datetime import datetime, timedelta
from trytond.model import fields
from trytond.model import ModelSQL, ModelView
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
import logging
from sql import Table
import traceback
logger = logging.getLogger(__name__)
class Cron(metaclass=PoolMeta):
__name__ = 'ir.cron'
@classmethod
def __setup__(cls):
super().__setup__()
cls.method.selection.append(
('automation.cron|update_shipment',
"Update Shipment from freight booking info")
)
class AutomationCron(ModelSQL, ModelView):
"Automation Cron"
__name__ = 'automation.cron'
frequency = fields.Selection([
('daily', "Daily"),
('weekly', "Weekly"),
('monthly', "Monthly"),
], "Frequency", required=True,
help="How frequently rates must be updated.")
last_update = fields.Date("Last Update", required=True)
@classmethod
def run(cls, crons):
cls.update_shipment()
@classmethod
def update_shipment(cls):
PoolObj = Pool()
ShipmentIn = PoolObj.get('stock.shipment.in')
Party = PoolObj.get('party.party')
Vessel = PoolObj.get('trade.vessel')
Location = PoolObj.get('stock.location')
# Table externe
t = Table('freight_booking_info')
cursor = Transaction().connection.cursor()
cursor.execute(*t.select(
t.ShippingInstructionNumber,
t.ShippingInstructionDate,
t.ShippingInstructionQuantity,
t.ShippingInstructionQuantityUnit,
t.NumberOfContainers,
t.ContainerType,
t.Loading,
t.Destination,
t.BookingAgent,
t.Carrier,
t.Vessel,
t.BL_Number,
t.ETD_Date,
t.BL_Date,
t.ExpectedController,
t.Comments,
t.FintradeBookingKey,
))
rows = cursor.fetchall()
logger.info(f"Nombre total de lignes a traiter : {len(rows)}")
# Premiere transaction : creation des objets de reference
with Transaction().new_transaction() as trans1:
try:
logger.info(
"Debut de la creation des objets de reference...")
parties_to_save = []
vessels_to_save = []
locations_to_save = []
parties_cache = {}
vessels_cache = {}
locations_cache = {}
# Collecter les donnees des objets de reference
for row in rows:
(
si_number, si_date, si_quantity, si_unit,
container_number, container_type,
loading_name, destination_name,
agent_name, carrier_name,
vessel_name, bl_number,
etd_date, bl_date, controller,
comments, fintrade_booking_key
) = row
def get_or_create_party(name):
if not name:
return None
name_upper = str(name).strip().upper()
if name_upper in parties_cache:
return parties_cache[name_upper]
existing = Party.search(
[('name', '=', name_upper)], limit=1)
if existing:
parties_cache[name_upper] = existing[0]
return existing[0]
new_p = Party()
new_p.name = name_upper
parties_cache[name_upper] = new_p
parties_to_save.append(new_p)
return new_p
def get_or_create_vessel(name):
if not name:
return None
name_upper = str(name).strip().upper()
if name_upper in vessels_cache:
return vessels_cache[name_upper]
existing = Vessel.search(
[('vessel_name', '=', name_upper)], limit=1)
if existing:
vessels_cache[name_upper] = existing[0]
return existing[0]
new_v = Vessel()
new_v.vessel_name = name_upper
vessels_cache[name_upper] = new_v
vessels_to_save.append(new_v)
return new_v
def get_or_create_location(name, type_):
if not name:
return None
name_upper = str(name).strip().upper()
key = f"{name_upper}_{type_}"
if key in locations_cache:
return locations_cache[key]
existing = Location.search([
('name', '=', name_upper),
('type', '=', type_),
], limit=1)
if existing:
locations_cache[key] = existing[0]
return existing[0]
new_loc = Location()
new_loc.name = name_upper
new_loc.type = type_
locations_cache[key] = new_loc
locations_to_save.append(new_loc)
return new_loc
_ = get_or_create_party(carrier_name)
_ = get_or_create_party(agent_name)
_ = get_or_create_vessel(vessel_name)
_ = get_or_create_location(loading_name, 'supplier')
_ = get_or_create_location(destination_name, 'customer')
if parties_to_save:
logger.info(f"Creation de {len(parties_to_save)} parties...")
Party.save(parties_to_save)
if vessels_to_save:
logger.info(f"Creation de {len(vessels_to_save)} vessels...")
Vessel.save(vessels_to_save)
if locations_to_save:
logger.info(
f"Creation de {len(locations_to_save)} locations...")
Location.save(locations_to_save)
trans1.commit()
logger.info(
"Premiere transaction commitee : objets de reference crees")
except Exception as e:
trans1.rollback()
logger.error(
f"Erreur dans la creation des objets de reference : {e}")
logger.error(traceback.format_exc())
raise
# Transactions individuelles pour chaque shipment
successful_shipments = 0
failed_shipments = []
cursor2 = Transaction().connection.cursor()
cursor2.execute(*t.select(
t.ShippingInstructionNumber,
t.ShippingInstructionDate,
t.ShippingInstructionQuantity,
t.ShippingInstructionQuantityUnit,
t.NumberOfContainers,
t.ContainerType,
t.Loading,
t.Destination,
t.BookingAgent,
t.Carrier,
t.Vessel,
t.BL_Number,
t.ETD_Date,
t.BL_Date,
t.ExpectedController,
t.Comments,
t.FintradeBookingKey,
))
rows2 = cursor2.fetchall()
for i, row in enumerate(rows2, 1):
(
si_number, si_date, si_quantity, si_unit,
container_number, container_type,
loading_name, destination_name,
agent_name, carrier_name,
vessel_name, bl_number,
etd_date, bl_date, controller,
comments, fintrade_booking_key
) = row
logger.info(f"Traitement shipment {i}/{len(rows2)} : SI {si_number}")
try:
with Transaction().new_transaction() as trans_shipment:
logger.info(f"Debut transaction pour SI {si_number}")
existing_shipment = ShipmentIn.search([
('reference', '=', si_number)
], limit=1)
if existing_shipment:
shipment = existing_shipment[0]
if shipment.incoming_moves:
logger.info(
"Shipment %s existe deja avec lots, ignore",
si_number)
trans_shipment.commit()
continue
logger.info(
"Shipment %s existe deja sans lots, verification freight_booking_lots",
si_number)
inv_date, inv_nb = shipment._create_lots_from_fintrade()
shipment = ShipmentIn(shipment.id)
if shipment.incoming_moves:
shipment.controller = shipment.get_controller()
shipment.controller_target = controller
if not shipment.fees:
shipment.create_fee(shipment.controller)
shipment.instructions = shipment.get_instructions_html(
inv_date, inv_nb)
ShipmentIn.save([shipment])
logger.info(
"Shipment %s mis a jour avec %s incoming move(s)",
si_number, len(shipment.incoming_moves))
else:
logger.info(
"Shipment %s existe sans lots et aucun lot disponible pour l'instant",
si_number)
trans_shipment.commit()
continue
carrier = None
if carrier_name:
carrier_list = Party.search([
('name', '=',
str(carrier_name).strip().upper())
], limit=1)
if carrier_list:
carrier = carrier_list[0]
logger.info(
f"Carrier trouve pour {si_number}: {carrier.name}")
else:
logger.warning(
f"Carrier NON TROUVE pour {si_number}: '{carrier_name}'")
agent = None
agent_list = Party.search([
('name', '=', str(agent_name or 'TBN').strip().upper())
], limit=1)
if agent_list:
agent = agent_list[0]
vessel = None
if vessel_name:
vessel_list = Vessel.search([
('vessel_name', '=',
str(vessel_name).strip().upper())
], limit=1)
if vessel_list:
vessel = vessel_list[0]
loc_from = None
if loading_name:
loc_from_list = Location.search([
('name', '=', str(loading_name).strip().upper()),
('type', '=', 'supplier')
], limit=1)
if loc_from_list:
loc_from = loc_from_list[0]
loc_to = None
if destination_name:
loc_to_list = Location.search([
('name', '=', str(destination_name).strip().upper()),
('type', '=', 'customer')
], limit=1)
if loc_to_list:
loc_to = loc_to_list[0]
if not carrier:
error_msg = (
f"ERREUR CRITIQUE: Carrier manquant pour SI {si_number} "
f"(valeur: '{carrier_name}')")
logger.error(error_msg)
raise ValueError(error_msg)
shipment = ShipmentIn()
shipment.reference = si_number
shipment.from_location = loc_from
shipment.to_location = loc_to
shipment.carrier = None # carrier
shipment.supplier = agent
shipment.agent = agent
shipment.vessel = vessel
shipment.cargo_mode = 'bulk'
shipment.bl_number = bl_number
shipment.bl_date = bl_date
shipment.etd = etd_date
shipment.etad = shipment.bl_date + timedelta(days=20)
ShipmentIn.save([shipment])
inv_date, inv_nb = shipment._create_lots_from_fintrade()
shipment.controller = shipment.get_controller()
shipment.controller_target = controller
shipment.create_fee(shipment.controller)
shipment.instructions = shipment.get_instructions_html(
inv_date, inv_nb)
ShipmentIn.save([shipment])
trans_shipment.commit()
successful_shipments += 1
logger.info(f"Shipment {si_number} cree avec succes")
except Exception as e:
error_details = {
'si_number': si_number,
'carrier_name': carrier_name,
'error': str(e),
'traceback': traceback.format_exc()
}
failed_shipments.append(error_details)
logger.error(f"ERREUR pour shipment {si_number}: {e}")
logger.error(f" Carrier: '{carrier_name}'")
logger.error(f" Agent: '{agent_name}'")
logger.error(f" Vessel: '{vessel_name}'")
logger.error(" Traceback complet:")
for line in traceback.format_exc().split('\n'):
if line.strip():
logger.error(f" {line}")
logger.info("=" * 60)
logger.info("RESUME DE L'EXECUTION")
logger.info("=" * 60)
logger.info(f"Total de shipments a traiter : {len(rows2)}")
logger.info(f"Shipments crees avec succes : {successful_shipments}")
logger.info(f"Shipments en echec : {len(failed_shipments)}")
if failed_shipments:
logger.info("\nDetail des echecs :")
for i, error in enumerate(failed_shipments, 1):
logger.info(f" {i}. SI {error['si_number']}:")
logger.info(f" Carrier: '{error['carrier_name']}'")
logger.info(f" Erreur: {error['error']}")
logger.info("\nAnalyse des carriers problematiques :")
problematic_carriers = {}
for error in failed_shipments:
carrier = error['carrier_name']
if carrier in problematic_carriers:
problematic_carriers[carrier] += 1
else:
problematic_carriers[carrier] = 1
for carrier, count in problematic_carriers.items():
logger.info(f" Carrier '{carrier}' : {count} echec(s)")
existing = Party.search([
('name', '=', str(carrier).strip().upper())
], limit=1)
if existing:
logger.info(
f" -> EXISTE DANS LA BASE (ID: {existing[0].id})")
else:
logger.info(" -> N'EXISTE PAS DANS LA BASE")
logger.info("=" * 60)

View File

@@ -0,0 +1,37 @@
<?xml version="1.0"?>
<tryton>
<data>
<record model="ir.ui.view" id="cron_view_list">
<field name="model">automation.cron</field>
<field name="type">tree</field>
<field name="name">cron_list</field>
</record>
<record model="ir.ui.view" id="cron_view_form">
<field name="model">automation.cron</field>
<field name="type">form</field>
<field name="name">cron_form</field>
</record>
<record model="ir.action.act_window" id="act_cron_form">
<field name="name">Update shipment from freight booking</field>
<field name="res_model">automation.cron</field>
</record>
<record model="ir.action.act_window.view" id="act_cron_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="cron_view_list"/>
<field name="act_window" ref="act_cron_form"/>
</record>
<record model="ir.action.act_window.view" id="act_cron_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="cron_view_form"/>
<field name="act_window" ref="act_cron_form"/>
</record>
<record model="ir.cron" id="cron_cron">
<field name="method">automation.cron|update_shipment</field>
<field name="interval_number" eval="1"/>
<field name="interval_type">days</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,72 @@
from trytond.model import ModelSQL, fields
class ExecutionFollowUp(ModelSQL):
"Execution Follow Up"
__name__ = 'execution.automation'
port_of_loading = fields.Char("Port of Loading")
fb_loading = fields.Char("FB Loading")
warehouse = fields.Char("Warehouse")
origin = fields.Char("Origin")
agent = fields.Char("Agent")
operator = fields.Char("Operator")
fintrade_lc_nb = fields.Char("Fintrade LC Nb")
lc_number = fields.Char("LC Number")
si = fields.Char("SI")
port_of_destination = fields.Char("Port of Destination")
fb_destination = fields.Char("FB Destination")
status = fields.Char("Status")
etd_date = fields.Date("ETD Date")
bl_date = fields.Date("BL Date")
etd_sob = fields.Date("ETD SOB")
sale_contract_no = fields.Char("Sale Contract No")
customer = fields.Char("Customer")
elt_quantity = fields.Float("Elt Quantity")
number_of_container = fields.Integer("Containers")
vessel = fields.Char("Vessel")
shipping_company = fields.Char("Shipping Company")
booking_ref = fields.Char("Booking Ref")
freight_forwarder = fields.Char("Freight Forwarder")
instrument_status = fields.Char("Instrument Status")
ip_date = fields.Date("IP Date")
ip_status = fields.Char("IP Status")
latest_shipment_date = fields.Date("Latest Shipment Date")
countersigned = fields.Boolean("Countersigned")
comments = fields.Text("Comments")
docs_internal_comments = fields.Text("Docs Internal Comments")
alloc_quantity = fields.Float("Allocated Quantity")
si_comments = fields.Text("SI Comments")
is_archived = fields.Boolean("Archived")
price_cont = fields.Numeric("Price / Cont")
price_cont_curr = fields.Char("Currency")
ct_period_start = fields.Date("CT Start")
ct_period_end = fields.Date("CT End")
lsd_check = fields.Char("LSD Check")
bl2lsd_delta = fields.Integer("BL → LSD Delta")
fintrade_booking = fields.Char("Fintrade Booking")
alloc_unit_price = fields.Numeric("Alloc Unit Price")
alloc_price_curr = fields.Char("Alloc Price Curr")
alloc_price_unit = fields.Char("Alloc Price Unit")
left_time = fields.Integer("Days Left")
@classmethod
def table_query(cls):
return (
"SELECT "
"row_number() OVER () AS id, "
"* "
"FROM singa_execution_follow_up"
)

View File

@@ -0,0 +1,9 @@
<tryton>
<data>
<record model="ir.ui.view" id="execution_followup_tree">
<field name="model">execution.automation</field>
<field name="type">tree</field>
<field name="name">execution_automation_tree</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,56 @@
from trytond.model import ModelSQL, ModelView, fields
from sql import Table
from sql.functions import CurrentTimestamp
from sql import Column, Literal
import logging
logger = logging.getLogger(__name__)
class FreightBookingInfo(ModelSQL, ModelView):
"Freight Booking"
__name__ = 'freight.booking.info'
booking_number = fields.Char("Booking Number")
agent = fields.Char("Agent")
controller = fields.Char("Customer")
origin = fields.Char("Origin")
destination = fields.Char("Destination")
etd = fields.Date("ETD")
bl_date = fields.Date("BL date")
bl_number = fields.Char("BL Nb")
carrier = fields.Char("Carrier")
vessel = fields.Char("Vessel")
container_count = fields.Float("Containers")
quantity = fields.Float("Gross Weight")
@classmethod
def table_query(cls):
t = Table('freight_booking_info')
query = t.select(
Literal(None).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
Column(t, 'FintradeBookingKey').as_('id'),
Column(t, 'ShippingInstructionNumber').as_('booking_number'),
Column(t, 'BookingAgent').as_('agent'),
Column(t, 'ExpectedController').as_('controller'),
Column(t, 'Loading').as_('origin'),
Column(t, 'Destination').as_('destination'),
Column(t, 'ETD_Date').as_('etd'),
Column(t, 'BL_Date').as_('bl_date'),
Column(t, 'BL_Number').as_('bl_number'),
Column(t, 'Carrier').as_('carrier'),
Column(t, 'Vessel').as_('vessel'),
Column(t, 'NumberOfContainers').as_('container_count'),
Column(t, 'ShippingInstructionQuantity').as_('quantity'),
)
#logger.info("*****QUERY*****:%s",query)
return query
@classmethod
def __setup__(cls):
super().__setup__()
cls._order = [
('etd', 'DESC'),
]

View File

@@ -0,0 +1,25 @@
<tryton>
<data>
<record model="ir.ui.view" id="freight_booking_info_tree">
<field name="model">freight.booking.info</field>
<field name="type">tree</field>
<field name="name">freight_booking_info_tree</field>
</record>
<record model="ir.action.act_window" id="act_freight_booking_info">
<field name="name">Freight Bookings</field>
<field name="res_model">freight.booking.info</field>
</record>
<record model="ir.action.act_window.view" id="act_freight_booking_info_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="freight_booking_info_tree"/>
<field name="act_window" ref="act_freight_booking_info"/>
</record>
<menuitem
name="Freight Booking"
action="act_freight_booking_info"
parent="menu_automation"
sequence="10"
id="menu_freight_booking" />
</data>
</tryton>

View File

@@ -5,4 +5,6 @@ depends:
res
document_incoming
xml:
automation.xml
automation.xml
freight_booking.xml
cron.xml

View File

@@ -0,0 +1,60 @@
<tree>
<field name="port_of_loading"/>
<field name="fb_loading"/>
<field name="warehouse"/>
<field name="origin"/>
<field name="agent"/>
<field name="operator"/>
<field name="fintrade_lc_nb"/>
<field name="lc_number"/>
<field name="si"/>
<field name="port_of_destination"/>
<field name="fb_destination"/>
<field name="status"/>
<field name="etd_date"/>
<field name="bl_date"/>
<field name="etd_sob"/>
<field name="sale_contract_no"/>
<field name="customer"/>
<field name="elt_quantity"/>
<field name="number_of_container"/>
<field name="vessel"/>
<field name="shipping_company"/>
<field name="booking_ref"/>
<field name="freight_forwarder"/>
<field name="instrument_status"/>
<field name="ip_date"/>
<field name="ip_status"/>
<field name="latest_shipment_date"/>
<field name="countersigned"/>
<field name="comments"/>
<field name="docs_internal_comments"/>
<field name="alloc_quantity"/>
<field name="si_comments"/>
<field name="is_archived"/>
<field name="price_cont"/>
<field name="price_cont_curr"/>
<field name="ct_period_start"/>
<field name="ct_period_end"/>
<field name="lsd_check"/>
<field name="bl2lsd_delta"/>
<field name="fintrade_booking"/>
<field name="alloc_unit_price"/>
<field name="alloc_price_curr"/>
<field name="alloc_price_unit"/>
<field name="left_time"/>
</tree>

View File

@@ -0,0 +1,13 @@
<tree>
<field name="booking_number"/>
<field name="agent"/>
<field name="controller"/>
<field name="origin"/>
<field name="destination"/>
<field name="etd"/>
<field name="bl_date"/>
<field name="bl_number"/>
<field name="carrier"/>
<field name="vessel"/>
<field name="container_count"/>
</tree>

View File

@@ -2,9 +2,4 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="//page[@id='costs']" position="inside">
<field name="before_carriages" colspan="4"/>
<field name="after_carriages" colspan="4"/>
</xpath>
</data>

View File

@@ -27,6 +27,14 @@ class Sale(metaclass=PoolMeta):
invoice.save()
return invoice
@property
def report_agent(self):
if self.agent:
return (self.agent.party.address_get(
type='delivery')).full_address
else:
return ''
@classmethod
@ModelView.button
@Workflow.transition('quotation')

View File

@@ -47,6 +47,8 @@ class Company(ModelSQL, ModelView):
help="Used to compute the today date.")
employees = fields.One2Many('company.employee', 'company', 'Employees',
help="Add employees to the company.")
logo = fields.Binary("Logo")
@property
def header_used(self):

View File

@@ -17,6 +17,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="header"/>
<separator name="footer"/>
<field name="footer"/>
<separator name="logo"/>
<field name="logo" widget="image" stretch="true"/>
</page>
</notebook>
</form>

View File

@@ -137,6 +137,13 @@ class Currency(
closer = date
return res
@classmethod
def get_by_name(cls, name):
currencies = cls.search([('symbol', '=', name)], limit=1)
if not currencies:
return None
return currencies[0]
@staticmethod
def _get_rate(currencies, tdate=None):
'''

View File

@@ -17,6 +17,8 @@ from trytond.transaction import Transaction
from trytond.wizard import Button, StateTransition, StateView, Wizard
from .exceptions import DocumentIncomingSplitError
import logging
logger = logging.getLogger(__name__)
if config.getboolean('document_incoming', 'filestore', default=True):
file_id = 'file_id'
@@ -179,30 +181,112 @@ class Incoming(DeactivableMixin, Workflow, ModelSQL, ModelView):
def _split_mime_types(cls):
return ['application/pdf']
# @classmethod
# def from_inbound_email(cls, email_, rule):
# message = email_.as_dict()
# attachments = message.get('attachments')
# active = False
# data = message.get('text', message.get('html'))
# logger.info("DATA_FROM_INBOUND_MAIL:%s",data)
# if isinstance(data, str):
# data = data.encode()
# body = message.get('text') or message.get('html') or ''
# if isinstance(body, str):
# body_bytes = body.encode('utf-8')
# else:
# body_bytes = body
# document = cls(
# active=active,
# name=message.get('subject', 'No Subject'),
# company=rule.document_incoming_company,
# data=data,
# type=rule.document_incoming_type if active else None,
# source='inbound_email',
# )
# children = []
# if attachments:
# for attachment in attachments:
# child = cls(
# name=attachment['filename'] or 'data.bin',
# company=rule.document_incoming_company,
# data=attachment['data'],
# type=rule.document_incoming_type,
# source='inbound_email')
# children.append(child)
# else:
# child = cls(
# name='mail_' + message.get('subject', 'No Subject') + '.txt',
# company=rule.document_incoming_company,
# data=body_bytes,
# type=rule.document_incoming_type,
# source='inbound_email',
# )
# children.append(child)
# document.children = children
# document.save()
# return document
@classmethod
def from_inbound_email(cls, email_, rule):
message = email_.as_dict()
active = not message.get('attachments')
def clean(value):
if not value:
return value
return (
value
.replace('\n', ' ')
.replace('\r', ' ')
.replace("'", '')
.replace('"', '')
.strip()
)
subject = clean(message.get('subject', 'No Subject'))
attachments = message.get('attachments')
active = False
data = message.get('text', message.get('html'))
logger.info("DATA_FROM_INBOUND_MAIL:%s", data)
if isinstance(data, str):
data = data.encode()
body = message.get('text') or message.get('html') or ''
if isinstance(body, str):
body_bytes = body.encode('utf-8')
else:
body_bytes = body
document = cls(
active=active,
name=message.get('subject', 'No Subject'),
name=subject,
company=rule.document_incoming_company,
data=data,
type=rule.document_incoming_type if active else None,
source='inbound_email',
)
)
children = []
for attachment in message.get('attachments', []):
if attachments:
for attachment in attachments:
filename = clean(attachment['filename'] or 'data.bin')
child = cls(
name=filename,
company=rule.document_incoming_company,
data=attachment['data'],
type=rule.document_incoming_type,
source='inbound_email')
children.append(child)
else:
child = cls(
name=attachment['filename'] or 'data.bin',
name='mail_' + subject + '.txt',
company=rule.document_incoming_company,
data=attachment['data'],
data=body_bytes,
type=rule.document_incoming_type,
source='inbound_email')
source='inbound_email',
)
children.append(child)
document.children = children
document.save()
return document
@@ -265,7 +349,6 @@ class Incoming(DeactivableMixin, Workflow, ModelSQL, ModelView):
default.setdefault('children')
return super().copy(documents, default=default)
def iter_pages(expression, size):
ranges = set()
for pages in expression.split(','):

View File

@@ -4,7 +4,8 @@
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
import logging
logger = logging.getLogger(__name__)
class Rule(metaclass=PoolMeta):
__name__ = 'inbound.email.rule'
@@ -53,4 +54,4 @@ class Rule(metaclass=PoolMeta):
if (self.action == 'document.incoming|from_inbound_email'
and self.document_incoming_process):
document = email_.result
DocumentIncoming.process([document], with_children=True)
DocumentIncoming.process([document], with_children=True)

View File

@@ -0,0 +1,15 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.pool import Pool
from . import document
__all__ = ['register']
def register():
Pool.register(
document.IncomingConfiguration,
document.Incoming,
module='document_incoming_wr', type_='model')

View File

@@ -0,0 +1,90 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.i18n import gettext
from trytond.model import fields
from trytond.modules.document_incoming.exceptions import (
DocumentIncomingProcessError)
from trytond.pool import Pool, PoolMeta
import json
class IncomingConfiguration(metaclass=PoolMeta):
__name__ = 'document.incoming.configuration'
default_controller = fields.Many2One('party.party', "Default Controller")
class Incoming(metaclass=PoolMeta):
__name__ = 'document.incoming'
created_at = fields.Function(
fields.Date("Created At"),
'get_created_at')
result_notes = fields.Function(
fields.Text("Result Notes"),
'get_result_notes')
@classmethod
def __setup__(cls):
super().__setup__()
cls.type.selection.append(
('weight_report', "Weight Report"))
cls.type.selection.append(
('controller', "Controller"))
cls._order = [('create_date', 'DESC')]
@classmethod
def _get_results(cls):
return super()._get_results() | {'automation.document'}
def _process_weight_report(self):
WR = Pool().get('automation.document')
wr = WR()
wr.document = self.id
wr.type = 'weight_report'
wr.state = 'draft'
WR.save([wr])
WR.run_ocr([wr])
WR.run_metadata([wr])
WR.run_pipeline([wr])
return wr
def _process_controller(self):
WR = Pool().get('automation.document')
wr = WR()
wr.document = self.id
wr.type = 'controller'
wr.state = 'draft'
WR.save([wr])
WR.run_ocr([wr])
# WR.run_metadata([wr])
return wr
def get_created_at(self, name=None):
create_date = getattr(self, 'create_date', None)
if not create_date:
return None
return create_date.date()
@classmethod
def order_created_at(cls, tables):
table, _ = tables[None]
return [table.create_date]
def get_result_notes(self, name=None):
result = getattr(self, 'result', None)
if not result:
return ''
if getattr(result, '__name__', None) == 'automation.document':
return getattr(result, 'notes', '') or ''
return ''
# @property
# def supplier_invoice_company(self):
# pass
# @property
# def supplier_invoice_party(self):
# pass

View File

@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="ddocument_incoming_configuration_view_form">
<field name="model">document.incoming.configuration</field>
<field name="inherit" ref="document_incoming.document_incoming_configuration_view_form"/>
<field name="name">document_incoming_configuration_form</field>
</record>
<record model="ir.ui.view" id="document_incoming_view_list_wr">
<field name="model">document.incoming</field>
<field name="inherit" ref="document_incoming.document_incoming_view_list"/>
<field name="name">document_incoming_list</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,10 @@
[tryton]
version=7.2.0
depends:
document_incoming
automation
ir
party
xml:
document.xml
view/document_incoming_list.xml

View File

@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/form" position="inside">
<separator string="Weight Report" id="weight_report" colspan="4"/>
<label name="default_controller"/>
<field name="default_controller"/>
</xpath>
</data>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="/tree/field[@name='name']" position="before">
<field name="created_at" expand="2"/>
</xpath>
<xpath expr="/tree/field[@name='result']" position="after">
<field name="result_notes" expand="2"/>
</xpath>
</data>

View File

@@ -17,6 +17,9 @@ from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.url import http_host
import logging
logger = logging.getLogger(__name__)
if config.getboolean('inbound_email', 'filestore', default=True):
file_id = 'data_id'
store_prefix = config.get('inbound_email', 'store_prefix', default=None)
@@ -74,6 +77,7 @@ class Inbox(ModelSQL, ModelView):
assert email_.inbox == self
for rule in self.rules:
if rule.match(email_.as_dict()):
logger.info("RULE_MATCHED:%s",rule)
email_.rule = rule
rule.run(email_)
return

View File

@@ -12,7 +12,7 @@ __all__ = ['IncotermMixin', 'IncotermAvailableMixin']
class IncotermMixin(Model):
incoterm = fields.Many2One(
'incoterm.incoterm', lazy_gettext('incoterm.msg_incoterm'),
'incoterm.incoterm', lazy_gettext('incoterm.msg_incoterm'), required=False,
ondelete='RESTRICT')
incoterm_location = fields.Many2One(
'party.address', lazy_gettext('incoterm.msg_incoterm_location'),

View File

@@ -24,8 +24,14 @@ class Lot(ModelSQL, ModelView):
lot_name = fields.Char("Lot")
number = fields.Char("Number", readonly=True)
lot_qt = fields.Float("Quantity",required=False)
lot_unit = fields.Many2One('product.uom', "Unit",required=False)
lot_qt = fields.Float("Quantity", required=False,
states={
'readonly': Eval('lot_type') == 'virtual',
})
lot_unit = fields.Many2One('product.uom', "Unit", required=False,
states={
'readonly': Eval('lot_type') == 'virtual',
})
lot_product = fields.Many2One('product.product', "Product")
lot_type = fields.Selection([
('virtual', 'Open'),
@@ -477,15 +483,16 @@ class Lot(ModelSQL, ModelView):
else:
return str(self.line.currency.symbol) + "/" + str(self.line.unit.symbol)
def get_hist_quantity(self,seq):
def get_hist_quantity(self,state_id=0):
qt = Decimal(0)
gross_qt = Decimal(0)
if self.lot_state:
if self.lot_hist:
if seq != 0:
st = seq
if state_id != 0:
st = state_id
else:
st = self.lot_state.id
logger.info("GET_HIST_QT:%s",st)
lot = [e for e in self.lot_hist if e.quantity_type.id == st][0]
qt = round(lot.quantity,5)
gross_qt = round(lot.gross_quantity,5)
@@ -499,24 +506,48 @@ class Lot(ModelSQL, ModelView):
physic_sum = Decimal(0)
for l in line.lots:
if l.lot_type == 'physic' :
physic_sum += round(Decimal(Uom.compute_qty(Uom(l.lot_unit_line),float(l.get_current_quantity()),l.line.unit)),5)
factor = None
rate = None
if l.lot_unit_line.category.id != l.line.unit.category.id:
factor = 1
rate = 1
physic_sum += round(Decimal(Uom.compute_qty(Uom(l.lot_unit_line),float(l.get_current_quantity()),l.line.unit, True, factor, rate)),5)
return line.quantity_theorical - physic_sum
def get_current_quantity(self,name=None):
# if self.lot_type == 'physic':
qt, gross_qt = self.get_hist_quantity(0)
qt, gross_qt = self.get_hist_quantity()
return qt
# else:
# return self.get_virtual_diff()
def get_current_quantity_converted(self,name=None):
def get_current_quantity_converted(self,state_id=0,unit=None):
Uom = Pool().get('product.uom')
unit = self.line.unit if self.line else self.sale_line.unit
return round(Decimal(Uom.compute_qty(self.lot_unit_line, float(self.get_current_quantity()), unit)),5)
if not unit:
unit = self.line.unit if self.line else self.sale_line.unit
qt, gross_qt = self.get_hist_quantity(state_id)
factor = None
rate = None
if self.lot_unit_line.category.id != unit.category.id:
factor = 1
rate = 1
return round(Decimal(Uom.compute_qty(self.lot_unit_line, float(qt), unit, True, factor, rate)),5)
def get_current_gross_quantity_converted(self,state_id=0,unit=None):
Uom = Pool().get('product.uom')
if not unit:
unit = self.line.unit if self.line else self.sale_line.unit
qt, gross_qt = self.get_hist_quantity(state_id)
factor = None
rate = None
if self.lot_unit_line.category.id != unit.category.id:
factor = 1
rate = 1
return round(Decimal(Uom.compute_qty(self.lot_unit_line, float(gross_qt), unit, True, factor, rate)),5)
def get_current_gross_quantity(self,name=None):
if self.lot_type == 'physic':
qt, gross_qt = self.get_hist_quantity(0)
qt, gross_qt = self.get_hist_quantity()
return gross_qt
else:
return None
@@ -526,6 +557,7 @@ class Lot(ModelSQL, ModelView):
lqh = LotQtHist()
lqh.quantity_type = qt_type
lqh.quantity = net
logger.info("ADD_QUANTITY_TO_HIST:%s",gross)
lqh.gross_quantity = gross
lqh.lot = self
return lqh
@@ -542,6 +574,7 @@ class Lot(ModelSQL, ModelView):
if existing:
hist = existing[0]
hist.quantity = net
logger.info("SET_CURRENT_HIST:%s",gross)
hist.gross_quantity = gross
else:
lot_hist.append(self.add_quantity_to_hist(net, gross, lqtt[0]))
@@ -633,6 +666,7 @@ class SplitLine(ModelView):
weight = fields.Numeric('Weight', digits=(16,5))
class SplitWizardStart(ModelView):
"Split Line Start"
__name__ = 'lot.split.wizard.start'
mode = fields.Selection([

View File

@@ -36,7 +36,7 @@
<notebook colspan="4">
<page string="General" id="general">
<field name="lot_childs" colspan="4" mode="tree,form" view_ids="lot.lot_view_tree_sequence2,lot.lot_view_form"/>
<field name="lot_hist" colspan="4"/>
<field name="lot_hist" colspan="4" readonly="1"/>
</page>
<page string="Accounting" id="accounting">
<field name="pivot" widget="html_viewer" height="600" colspan="4"/>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0"?>
<form col="4">
<label name="lot"/>
<field name="lot"/>
<field name="lot" readonly="1"/>
<label name="quantity_type"/>
<field name="quantity_type"/>
<field name="quantity_type" readonly="1"/>
<label name="quantity"/>
<field name="quantity"/>
<field name="quantity" readonly="1"/>
<label name="gross_quantity"/>
<field name="gross_quantity"/>
<field name="gross_quantity" readonly="1"/>
</form>

View File

@@ -1,4 +1,4 @@
<tree editable="1">
<tree>
<field name="lot"/>
<field name="quantity_type"/>
<field name="quantity"/>

View File

@@ -1,4 +1,4 @@
<tree editable="1">
<tree>
<field name="lot"/>
<field name="quantity_type"/>
<field name="quantity"/>

View File

@@ -44,7 +44,7 @@ class Price(
price_composite = fields.One2Many('price.composite','price',"Composites")
price_product = fields.One2Many('price.product', 'price', "Product")
price_ct_size = fields.Numeric("Ct size")
def get_qt(self,nb_ct,unit):
Uom = Pool().get('product.uom')
return round(Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit)),4)
@@ -68,18 +68,22 @@ class Price(
amount = Decimal(self.get_price_per_qt(price,unit,currency)) * Decimal(Uom.compute_qty(self.price_unit, float(self.price_ct_size * nb_ct), unit))
return round(amount,4)
def get_price(self,dt,unit,currency,last=False):
price = float(0)
PV = Pool().get('price.price_value')
logger.info("ASKED_PRICE_FOR:%s",dt)
if self.price_values:
dt = dt.strftime("%Y-%m-%d")
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
if not pv and last:
pv = PV.search([('price','=',self.id)],order=[('price_date', 'DESC')])
if pv:
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
return round(price,4)
def get_price(self,dt,unit,currency,last=False, relative_last=False):
price = float(0)
PV = Pool().get('price.price_value')
if self.price_values:
dt = dt.strftime("%Y-%m-%d")
pv = PV.search([('price','=',self.id),('price_date','=',dt)])
if not pv and last:
pv = PV.search([('price','=',self.id)],order=[('price_date', 'DESC')])
elif not pv and relative_last:
pv = PV.search([
('price','=',self.id),
('price_date','<=',dt),
], order=[('price_date', 'DESC')])
if pv:
price = self.get_price_per_qt(pv[0].price_value,unit,currency)
return round(price,4)
class FixType(ModelSQL,ModelView):
"Fixation type"
@@ -115,7 +119,6 @@ class Calendar(DeactivableMixin,ModelSQL,ModelView,MultiValueMixin):
dt = dt.strftime("%Y-%m-%d")
cl = CL.search([('calendar','=',self.id),('price_date','=',dt)])
if cl:
#logger.info("ISQUOTE:%s",cl)
return False
else:
return True
@@ -136,3 +139,20 @@ class Product(ModelSQL,ModelView):
__name__ = 'price.product'
price = fields.Many2One('price.price',"Price index")
product = fields.Many2One('product.product',"Product")
attributes = fields.Many2One('product.attribute',"Attribute",domain=[
('sets', '=', Eval('attribute_set')),
],
states={
'readonly': ~Eval('attribute_set'),
},
depends=['product', 'attribute_set'])
attribute_set = fields.Function(
fields.Many2One('product.attribute.set', "Attribute Set"),
'on_change_with_attribute_set'
)
@fields.depends('product')
def on_change_with_attribute_set(self, name=None):
if self.product and self.product.template and self.product.template.attribute_set:
return self.product.template.attribute_set.id

View File

@@ -35,10 +35,11 @@ class PriceValue(
}), 'get_price_index')
price_date = fields.Date("Price date")
price_value = fields.Float("Price value")
open_price = fields.Float("Open price")
low_price = fields.Float("Low price")
high_price = fields.Float("High price")
price_value = fields.Float("Price value")
open_price = fields.Float("Open price")
low_price = fields.Float("Low price")
mid_price = fields.Float("Mid price")
high_price = fields.Float("High price")
def get_price_index(self, name):
if self.price:
@@ -55,9 +56,10 @@ class PriceValueReport(
r_price_desc = fields.Char("Description")
r_price_date = fields.Date("Price date")
r_price_value = fields.Float("Price")
r_open_price = fields.Float("Open price")
r_low_price = fields.Float("Low price")
r_high_price = fields.Float("High price")
r_open_price = fields.Float("Open price")
r_low_price = fields.Float("Low price")
r_mid_price = fields.Float("Mid price")
r_high_price = fields.Float("High price")
@classmethod
def table_query(cls):
@@ -86,10 +88,11 @@ class PriceValueReport(
pp.price_desc.as_('r_price_desc'),
pv.price_date.as_('r_price_date'),
pv.price_value.as_('r_price_value'),
pv.open_price.as_('r_open_price'),
pv.low_price.as_('r_low_price'),
pv.high_price.as_('r_high_price'),
where=wh)
pv.open_price.as_('r_open_price'),
pv.low_price.as_('r_low_price'),
pv.mid_price.as_('r_mid_price'),
pv.high_price.as_('r_high_price'),
where=wh)
return query
@@ -116,9 +119,10 @@ class PriceValueReport2(
r_price_desc = fields.Char("Description")
r_price_date = fields.Date("Price date")
r_price_value = fields.Float("Price")
r_open_price = fields.Float("Open price")
r_low_price = fields.Float("Low price")
r_high_price = fields.Float("High price")
r_open_price = fields.Float("Open price")
r_low_price = fields.Float("Low price")
r_mid_price = fields.Float("Mid price")
r_high_price = fields.Float("High price")
r_naphta_fob_low = fields.Float("Naph F Med l", digits=(1,2), help="Naphta Fob PAAAI00 low")
r_naphta_fob_high = fields.Float("Naph F Med h", digits=(1,2), help="Naphta Fob PAAAI00 high")
r_naphta_fob_mid = fields.Float("Naph F Med m", digits=(1,2), help="Naphta Fob PAAAI00 mid")
@@ -171,9 +175,10 @@ class PriceValueReport2(
Max(pp.price_desc).as_('r_price_desc'),
pv.price_date.as_('r_price_date'),
Max(pv.price_value).as_('r_price_value'),
Max(pv.open_price).as_('r_open_price'),
Max(pv.low_price).as_('r_low_price'),
Max(pv.high_price).as_('r_high_price'),
Max(pv.open_price).as_('r_open_price'),
Max(pv.low_price).as_('r_low_price'),
Max(pv.mid_price).as_('r_mid_price'),
Max(pv.high_price).as_('r_high_price'),
Max(Case((pv.price==15, pv.low_price),else_=0)).as_('r_naphta_fob_low'),
Max(Case((pv.price==15, pv.high_price),else_=0)).as_('r_naphta_fob_high'),
Max(Case((pv.price==15, (pv.high_price-pv.low_price)/2 + pv.low_price),else_=0)).as_('r_naphta_fob_mid'),

View File

@@ -2,8 +2,9 @@
<tree>
<field name="r_price_index"/>
<field name="r_price_desc"/>
<field name="r_price_date"/>
<field name="r_price_value"/>
<field name="r_low_price"/>
<field name="r_high_price"/>
</tree>
<field name="r_price_date"/>
<field name="r_price_value"/>
<field name="r_low_price"/>
<field name="r_mid_price"/>
<field name="r_high_price"/>
</tree>

View File

@@ -8,10 +8,12 @@
<label name="price_value"/>
<field name="price_value"/>
<label name="open_price"/>
<field name="open_price"/>
<label name="low_price"/>
<field name="low_price"/>
<label name="high_price"/>
<field name="high_price"/>
<field name="open_price"/>
<label name="low_price"/>
<field name="low_price"/>
<label name="mid_price"/>
<field name="mid_price"/>
<label name="high_price"/>
<field name="high_price"/>
</group>
</form>

View File

@@ -9,6 +9,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="price_value" fill="1" empty="0"/>
<field name="open_price" fill="1" empty="0"/>
<field name="low_price" fill="1" empty="0"/>
<field name="mid_price" fill="1" empty="0"/>
<field name="high_price" fill="1" empty="0"/>
</y>
</graph>

View File

@@ -2,8 +2,9 @@
<tree>
<field name="price_index"/>
<field name="price_date"/>
<field name="price_value"/>
<field name="open_price"/>
<field name="low_price"/>
<field name="high_price"/>
</tree>
<field name="price_value"/>
<field name="open_price"/>
<field name="low_price"/>
<field name="mid_price"/>
<field name="high_price"/>
</tree>

View File

@@ -2,8 +2,9 @@
<tree editable="1">
<field name="price_index"/>
<field name="price_date"/>
<field name="price_value"/>
<field name="open_price"/>
<field name="low_price"/>
<field name="high_price"/>
</tree>
<field name="price_value"/>
<field name="open_price"/>
<field name="low_price"/>
<field name="mid_price"/>
<field name="high_price"/>
</tree>

View File

@@ -1,6 +1,6 @@
<form>
<label name="price"/>
<field name="price"/>
<label name="product"/>
<field name="product"/>
<label name="attributes"/>
<field name="attributes"/>
</form>

View File

@@ -1,4 +1,5 @@
<tree>
<field name="price"/>
<field name="product"/>
<field name="attributes"/>
</tree>

View File

@@ -609,6 +609,26 @@ class Product(
('template.code', operator, code_value, *extra),
]
@classmethod
def get_by_name(cls, name, type_='goods'):
pool = Pool()
Template = pool.get('product.template')
Uom = pool.get('product.uom')
templates = Template.search([('name', '=', name)], limit=1)
if templates:
return templates[0].products[0]
unit_uom, = Uom.search([('name', '=', 'Mt')], limit=1)
template, = Template.create([{
'name': name,
'type': type_,
'default_uom': unit_uom.id,
'cost_price_method': 'fixed',
}])
return template.products[0]
@staticmethod
def get_price_uom(products, name):
Uom = Pool().get('product.uom')

View File

@@ -92,6 +92,13 @@ class Uom(SymbolMixin, DigitsMixin, DeactivableMixin, ModelSQL, ModelView):
def default_digits():
return 2
@classmethod
def get_by_name(cls, name):
uom = cls.search([('symbol', '=', name)], limit=1)
if not uom:
return None
return uom[0]
@fields.depends('factor')
def on_change_factor(self):
if (self.factor or 0.0) == 0.0:

View File

@@ -22,6 +22,7 @@ class Month(ModelView, ModelSQL):
is_cotation = fields.Boolean("Cotation month")
beg_date = fields.Date("Date from")
end_date = fields.Date("Date end")
description = fields.Char("Description")
class ProductMonth(ModelView, ModelSQL):
"Product month"

105
modules/purchase/AGENTS.md Normal file
View File

@@ -0,0 +1,105 @@
# AGENTS.md - Module `purchase`
Ce guide complete le `AGENTS.md` racine.
Pour ce module, les regles locales ci-dessous priment.
## 1) Perimetre metier
Le module `purchase` gere le cycle d'achat fournisseur:
- commande d'achat (`purchase.purchase`, `purchase.line`)
- facturation fournisseur (`account.invoice` liee a l'achat)
- reception/retour de stock (`stock.move`, `stock.shipment.in`, `stock.shipment.in.return`)
- reporting achats (axes temporels, fournisseur, produit)
## 2) Fichiers pivots
- Logique coeur:
- `modules/purchase/purchase.py`
- Extensions metier connexes:
- `modules/purchase/product.py`
- `modules/purchase/stock.py`
- `modules/purchase/invoice.py`
- `modules/purchase/party.py`
- `modules/purchase/configuration.py`
- `modules/purchase/purchase_reporting.py`
- Vues et actions:
- `modules/purchase/purchase.xml`
- `modules/purchase/stock.xml`
- `modules/purchase/invoice.xml`
- `modules/purchase/purchase_reporting.xml`
- Manifest et dependances:
- `modules/purchase/tryton.cfg`
- Documentation metier:
- `modules/purchase/docs/business-rules.template.md` (template)
- `modules/purchase/docs/business-rules.md` (instance a remplir)
## 3) Etats et flux critiques a preserver
Workflow de commande (dans `purchase.py`):
- `draft -> quotation -> confirmed -> processing -> done`
- transitions de retour existent aussi (`cancelled`, retour a `draft`, etc.)
Invariants importants:
- `invoice_state` et `shipment_state` doivent rester coherents apres `process()`.
- `process()` orchestre facture + stock + recalcul d'etats, ne pas contourner sans raison.
- `delete()` exige une commande annulee.
- Les methodes `create_invoice()` et `create_move()` sont sensibles (gestion `lots` et `action`).
## 4) Couplages a surveiller
- Facture:
- `purchase.py` <-> `invoice.py`
- gestion des exceptions facture (`purchase_exception_state`)
- Stock:
- `purchase.py` <-> `stock.py`
- liens `moves`, expeditions entrantes, retours
- Produit/fournisseur/prix:
- `product.py` impacte prix d'achat, UoM, fournisseurs
- Tiers:
- `party.py` impacte adresses/parametres fournisseur et contraintes d'effacement
## 5) Convention de modification pour ce module
1. Modifier d'abord le coeur minimal dans `purchase.py` ou le fichier specialise adequat.
2. Mettre a jour XML uniquement si comportement UI/action change.
3. Si regle metier impactee, mettre a jour `docs/business-rules.md`.
4. Ajouter un test proche du flux reel (scenario `.rst` prioritaire si possible).
5. Verifier les impacts transverses facture/stock avant rendu.
## 6) Strategie de test recommandee
Priorite 1 (rapide):
- `modules/purchase/tests/test_module.py`
Priorite 2 (comportement metier):
- `modules/purchase/tests/test_scenario.py`
- Scenarios cibles selon la modif:
- `scenario_purchase.rst`
- `scenario_purchase_manual_invoice.rst`
- `scenario_purchase_line_cancelled.rst`
- `scenario_purchase_line_cancelled_on_shipment.rst`
- `scenario_purchase_return_wizard.rst`
- `scenario_purchase_reporting.rst`
Si la modif touche prix/UoM/fournisseur:
- ajouter un cas dans `test_module.py` ou un scenario dedie.
## 7) Cas qui exigent validation humaine
- Changement du workflow d'etats
- Changement des regles de creation facture/mouvement
- Changement de logique sur retours fournisseur
- Changement qui altere les ecritures comptables ou le statut de paiement
## 8) Definition of done (module `purchase`)
- Le flux metier cible fonctionne de bout en bout.
- Les etats `state`, `invoice_state`, `shipment_state` restent coherents.
- Les tests du module pertinents passent.
- Le patch est limite aux fichiers necessaires.

View File

@@ -0,0 +1,122 @@
# Business Rules Template - Purchase
Statut: `draft` | `reviewed` | `approved`
Version: `v0.1`
Derniere mise a jour: `YYYY-MM-DD`
Owner metier: `Nom / Equipe`
Owner technique: `Nom / Equipe`
## 1) Scope
- Domaine: `ex: achats fournisseur`
- Hors scope: `ex: achats intercompany`
- Modules impactes:
- `purchase`
- `stock` (si applicable)
- `account_invoice` (si applicable)
## 2) Glossaire
- `Purchase`: commande d'achat fournisseur.
- `Line`: ligne de commande.
- `Invoice State`: etat facture calcule.
- `Shipment State`: etat reception calcule.
- Ajouter ici les termes metier propres a ton contexte.
## 3) Regles metier (source de verite)
### BR-001 - [Titre court]
- Intent: `Pourquoi cette regle existe`
- Description:
- `Enonce clair et testable`
- Conditions d'entree:
- `Etat`
- `Type de ligne (goods/service)`
- `Contexte (societe, devise, fournisseur, lot, etc.)`
- Resultat attendu:
- `Etat/valeur/action attendue`
- Exceptions:
- `Cas ou la regle ne s'applique pas`
- Priorite:
- `bloquante | importante | informative`
- Source:
- `Ticket / spec / decision metier`
### BR-002 - [Titre court]
- Intent:
- Description:
- Conditions d'entree:
- Resultat attendu:
- Exceptions:
- Priorite:
- Source:
## 4) Matrice d'etats (optionnel mais recommande)
| Regle | Etat initial | Evenement | Etat attendu | Notes |
|---|---|---|---|---|
| BR-001 | `draft` | `quote` | `quotation` | |
| BR-002 | `quotation` | `confirm` | `confirmed/processing` | |
## 5) Exemples concrets
### Exemple E1 - Cas nominal
- Donnees:
- `fournisseur = X`
- `produit = Y`
- `quantite = 10`
- Attendu:
- `invoice_state = pending`
- `shipment_state = waiting`
### Exemple E2 - Cas limite
- Donnees:
- Attendu:
## 6) Impact code attendu
- Fichiers Python potentiellement concernes:
- `modules/purchase/purchase.py`
- `modules/purchase/stock.py`
- `modules/purchase/invoice.py`
- `modules/purchase/product.py`
- Fichiers XML potentiellement concernes:
- `modules/purchase/purchase.xml`
- `modules/purchase/stock.xml`
- `modules/purchase/invoice.xml`
## 7) Strategie de tests
- Unitaires:
- `modules/purchase/tests/test_module.py`
- Scenarios:
- `modules/purchase/tests/scenario_purchase.rst`
- `modules/purchase/tests/scenario_purchase_manual_invoice.rst`
- `modules/purchase/tests/scenario_purchase_return_wizard.rst`
Pour chaque regle BR-xxx, lister le test associe:
| Regle | Test existant | Nouveau test a ajouter | Statut |
|---|---|---|---|
| BR-001 | `...` | `...` | `todo` |
## 8) Compatibilite et migration
- Effet retroactif sur commandes existantes: `oui/non`
- Migration necessaire: `oui/non`
- Plan de rollback:
- `comment revenir en arriere sans corruption metier`
## 9) Validation
- Valide par metier:
- `Nom` - `date`
- Valide par technique:
- `Nom` - `date`
- Decision finale:
- `approved / rejected / needs update`

Some files were not shown because too many files have changed in this diff Show More