1473 Commits

Author SHA1 Message Date
d7bc4a4b88 Tower: upload laundry_management 19.0.19.0.4 (was 19.0.19.0.4, via marketplace) 2026-05-02 11:57:30 +00:00
ee9b1958f1 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 11:23:15 +00:00
48db592326 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 11:23:12 +00:00
8f834373b7 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 11:23:09 +00:00
c6765e04f7 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 11:23:03 +00:00
fc97d80c2c Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 11:22:59 +00:00
46ec5c998d Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 11:22:55 +00:00
f6f341c372 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 11:22:50 +00:00
3d159a89eb Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 11:22:46 +00:00
OdooSky Tower
a6a54b1f3f Cleanup: remove orphan addon source base_accounting_kit (no tags reference it) 2026-05-02 13:20:26 +02:00
763268a1e9 Tower: unpublish bi_print_journal_entries — remove source from 19.0 branch 2026-05-02 11:16:15 +00:00
e2efba2971 Tower: unpublish om_recurring_payments — remove source from 19.0 branch 2026-05-02 11:16:09 +00:00
7428a903a8 Tower: unpublish om_fiscal_year — remove source from 19.0 branch 2026-05-02 11:16:03 +00:00
4a71eef134 Tower: unpublish om_account_budget — remove source from 19.0 branch 2026-05-02 11:15:57 +00:00
e9b6ac9bbb Tower: unpublish om_account_asset — remove source from 19.0 branch 2026-05-02 11:15:52 +00:00
9304592ad6 Tower: unpublish om_account_accountant — remove source from 19.0 branch 2026-05-02 11:15:46 +00:00
26ae0e14df Tower: unpublish laundry_management — remove source from 19.0 branch 2026-05-02 11:15:37 +00:00
22162f9d47 Tower: unpublish accounting_pdf_reports — remove source from 19.0 branch 2026-05-02 11:15:27 +00:00
7e07a27a97 Tower: unpublish om_account_followup — remove source from 19.0 branch 2026-05-02 11:15:05 +00:00
0da904b92b Tower: unpublish om_account_daily_reports — remove source from 19.0 branch 2026-05-02 11:13:50 +00:00
9b4ce3b586 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) (force-overwrite) 2026-05-02 07:39:48 +00:00
49420992f5 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) (force-overwrite) 2026-05-02 07:39:42 +00:00
85d8f421c6 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) (force-overwrite) 2026-05-02 07:39:36 +00:00
966b74e9a3 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) (force-overwrite) 2026-05-02 07:39:28 +00:00
e9d0895fc9 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) (force-overwrite) 2026-05-02 07:39:09 +00:00
1ccd4e2f78 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) (force-overwrite) 2026-05-02 07:38:53 +00:00
e93e98f302 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) (force-overwrite) 2026-05-02 07:38:42 +00:00
9e8b644dc6 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) (force-overwrite) 2026-05-02 07:38:23 +00:00
8f624fee10 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) (force-overwrite) 2026-05-02 07:27:30 +00:00
efe8ee068a Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) (force-overwrite) 2026-05-02 07:27:24 +00:00
7ae04247ce Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) (force-overwrite) 2026-05-02 07:27:15 +00:00
d05b1f4bab Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) (force-overwrite) 2026-05-02 07:27:01 +00:00
96eeaa2d51 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) (force-overwrite) 2026-05-02 07:26:41 +00:00
69ae27892d Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) (force-overwrite) 2026-05-02 07:26:16 +00:00
c9a8cb1792 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) (force-overwrite) 2026-05-02 07:26:07 +00:00
120bff231c Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) (force-overwrite) 2026-05-02 07:25:53 +00:00
7f6aeebbe1 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:39 +00:00
3f66de30c4 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:37 +00:00
389ad788e3 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:36 +00:00
cc13e048b2 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:34 +00:00
085380a411 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:32 +00:00
1f3176a7b7 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:31 +00:00
88f26e9397 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:29 +00:00
2549ce8bb7 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:29 +00:00
1e585a419d Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:27 +00:00
9d3ef18528 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:26 +00:00
df40adb5db Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:25 +00:00
9fc91ed47a Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:24 +00:00
ef1ef68222 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:22 +00:00
aaeb29171b Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:20 +00:00
1a80cd8aa8 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:19 +00:00
a02c02b8e5 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:18 +00:00
720966186a Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:16 +00:00
d8924a234a Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:14 +00:00
a5d21a8853 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:13 +00:00
cca846b84e Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:12 +00:00
b88dcbfaa8 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:10 +00:00
8c7bde04d7 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:09 +00:00
52fcc8afa5 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:08 +00:00
e71b7a33c4 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:07 +00:00
529d640d2c Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:06 +00:00
db06b274c8 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:04 +00:00
d51e343b0e Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:02:01 +00:00
724e09d35b Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:59 +00:00
f46eca6af8 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:59 +00:00
cc3d0f3a6f Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:57 +00:00
693cd33119 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:56 +00:00
98c4ac4167 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:55 +00:00
8392ae58d9 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:53 +00:00
a25f5df019 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:51 +00:00
d7983aab11 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:50 +00:00
54ff49035e Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:49 +00:00
a2172f4581 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:47 +00:00
85147b59a1 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:45 +00:00
c5e332bf92 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:43 +00:00
fda43a256b Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:40 +00:00
2b34b6680a Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:37 +00:00
8af5977dc1 Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:35 +00:00
298759fdcd Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:33 +00:00
33b4abdfca Tower: upload om_account_followup 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 07:01:30 +00:00
cee7dfbc8c Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:27 +00:00
4651545305 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:24 +00:00
05e9d292ab Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:22 +00:00
d3d43cecb8 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:19 +00:00
227e71b14f Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:16 +00:00
d10ef5216c Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:14 +00:00
f1ed7c92f7 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:12 +00:00
1cc27af9ac Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:10 +00:00
a4b715534d Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:08 +00:00
16548c9204 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:05 +00:00
7acc979f33 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:01:02 +00:00
f4a7d664f2 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:59 +00:00
b596416791 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:56 +00:00
310de14ee8 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:54 +00:00
f626c90b77 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:52 +00:00
23cede2cec Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:49 +00:00
cc0e6493ae Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:47 +00:00
23cc52e9dc Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:46 +00:00
0f353f1df9 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:44 +00:00
317cdd1618 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:42 +00:00
c25e179e67 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:39 +00:00
59cdd6db51 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:37 +00:00
998ab2b18a Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:35 +00:00
6c6e2a6d06 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:32 +00:00
cba963b090 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:30 +00:00
273ca36a82 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:29 +00:00
cbcb69f040 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:27 +00:00
01b304ad67 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:24 +00:00
a3235eb5e7 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:22 +00:00
24f3e5b775 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:19 +00:00
361c3708dc Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:16 +00:00
923f087b7a Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:14 +00:00
590c130efa Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:12 +00:00
23249091b1 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:10 +00:00
a255dc7aae Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:07 +00:00
66a9f804ff Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 07:00:03 +00:00
c07af0a20f Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:59:59 +00:00
6b42a1a341 Tower: upload om_account_asset 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:59:57 +00:00
580d950eab Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:54 +00:00
08f8103f4f Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:51 +00:00
2c856313a5 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:48 +00:00
ce5240738c Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:47 +00:00
69804d0600 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:44 +00:00
5e7f44f96e Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:41 +00:00
1325283047 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:39 +00:00
097e327943 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:37 +00:00
1e2d165c13 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:34 +00:00
4b8cb108fa Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:32 +00:00
b458358351 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:30 +00:00
6d4b091bd0 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:28 +00:00
a473e89adb Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:26 +00:00
9035144daa Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:24 +00:00
8c11e98400 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:21 +00:00
4c6a032214 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:19 +00:00
89c01cde3a Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:17 +00:00
335ae7cc37 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:15 +00:00
aeb0dfa32b Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:13 +00:00
ab0fcc907d Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:12 +00:00
488373ef1d Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:09 +00:00
ed88e8b964 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:06 +00:00
c2fd100b77 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:03 +00:00
6c358ca955 Tower: upload om_fiscal_year 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:59:00 +00:00
2f387e8a65 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:56 +00:00
efaeade746 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:54 +00:00
599e09df61 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:52 +00:00
52c65aa9b5 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:49 +00:00
41c4ff034a Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:47 +00:00
ced6377266 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:44 +00:00
fb04b47190 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:41 +00:00
cd6812f3b8 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:39 +00:00
84183488f8 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:36 +00:00
2c552e1c97 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:34 +00:00
b4810b9f98 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:31 +00:00
5dbfced933 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:28 +00:00
afc1b45d6a Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:26 +00:00
5ff0a2841e Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:24 +00:00
59199e3702 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:20 +00:00
93e36e7e29 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:17 +00:00
4618b061c8 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:14 +00:00
626ede148f Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:09 +00:00
cad549b100 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:05 +00:00
953b69ca28 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:58:02 +00:00
ed9fdb17f3 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:58 +00:00
9ba4a97fe3 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:53 +00:00
c97dd79db1 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:49 +00:00
f2c47e8c13 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:43 +00:00
0c09645e6d Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:39 +00:00
48aee47770 Tower: upload om_account_budget 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:36 +00:00
cb3d25ce0f Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:31 +00:00
b6660d899a Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:27 +00:00
b22ccc147f Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:25 +00:00
5e1b2881db Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:21 +00:00
59397bbbaf Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:17 +00:00
43650a81e8 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:13 +00:00
e9114765c0 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:10 +00:00
f8e7bcad5d Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:06 +00:00
d919f48451 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:57:01 +00:00
81ce31a57a Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:58 +00:00
f95eca7b2e Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:54 +00:00
d353bd94ab Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:52 +00:00
f6d3119fdc Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:47 +00:00
7903652796 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:44 +00:00
c0d4958555 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:39 +00:00
4f21f98520 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:36 +00:00
dfcda13313 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:31 +00:00
6b0de8a446 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:30 +00:00
148c3a12d9 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:28 +00:00
357c73e7d1 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:26 +00:00
0553a3faf7 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:23 +00:00
12c3d9dcc0 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:20 +00:00
23b40d5922 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:19 +00:00
3f63f8d209 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:15 +00:00
6fd4de2cc8 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:11 +00:00
794edc73a0 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:08 +00:00
bf822b8178 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:05 +00:00
875db440fc Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:56:04 +00:00
76be4825f1 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:55:59 +00:00
77ff5a54f2 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:55:58 +00:00
a33c2380a2 Tower: upload om_account_daily_reports 19.0.1.0.1 (was 1.0.1, via marketplace) 2026-05-02 06:55:55 +00:00
c9d191cd7c Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:51 +00:00
e5882ef4fc Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:49 +00:00
0afa863d76 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:45 +00:00
b6fe704976 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:41 +00:00
afe3108bac Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:38 +00:00
940cd44458 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:34 +00:00
4982f777ca Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:32 +00:00
f2794c7b4b Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:28 +00:00
0b1545593b Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:24 +00:00
0a026f7122 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:21 +00:00
d3b616b5e9 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:19 +00:00
67d9c5a66d Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:15 +00:00
27973de8db Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:13 +00:00
d21fc69654 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:10 +00:00
8ef99a0351 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:09 +00:00
15dba9eeac Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:06 +00:00
5b5bc26980 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:04 +00:00
8f72d4c181 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:55:00 +00:00
689a600475 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:58 +00:00
125229bf32 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:56 +00:00
311491585e Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:54 +00:00
89215de3cc Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:51 +00:00
3ed3987ff2 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:49 +00:00
bc62e0679e Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:48 +00:00
4540f89d56 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:45 +00:00
76a9794601 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:43 +00:00
2f74414402 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:41 +00:00
b7c000ec2b Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:39 +00:00
9bbea37ffa Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:37 +00:00
bd51b90454 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:35 +00:00
410f68fd85 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:32 +00:00
6264906b96 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:28 +00:00
de1c0a7a97 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:26 +00:00
5a4b57e071 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:23 +00:00
fa7ec32530 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:20 +00:00
79c0e0c7df Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:18 +00:00
551c5ff078 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:14 +00:00
cee6b58491 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:12 +00:00
71749fa98b Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:09 +00:00
0bf6239825 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:07 +00:00
b69300873e Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:04 +00:00
013f1ebf8a Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:54:02 +00:00
2392132751 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:58 +00:00
3885674952 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:57 +00:00
3933decc5d Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:56 +00:00
98e3afdfa6 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:53 +00:00
25e374d814 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:49 +00:00
9a9a0d093e Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:44 +00:00
d8c641942e Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:40 +00:00
a72d7b7497 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:35 +00:00
699905af74 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:30 +00:00
1dbf28c898 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:25 +00:00
135cd35e8e Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:23 +00:00
e7a07af65a Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:19 +00:00
8f92c36a90 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:16 +00:00
1139606ba9 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:12 +00:00
5e7dc1dace Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:08 +00:00
30500a1a79 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:05 +00:00
49e35bb590 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:53:01 +00:00
1de375ee02 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:58 +00:00
a35e329b22 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:53 +00:00
7323f4e828 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:51 +00:00
e2cb07e990 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:49 +00:00
98d315cef4 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:48 +00:00
78b95aff58 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:46 +00:00
bf5b83a46d Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:42 +00:00
452a529dca Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:40 +00:00
7e3704ba35 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:38 +00:00
6ad452e5af Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:34 +00:00
6911b4e06e Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:30 +00:00
ff240d6e5f Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:27 +00:00
12125792f2 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:24 +00:00
bb945c2bbe Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:20 +00:00
be8bec6163 Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:18 +00:00
2d85f731af Tower: upload accounting_pdf_reports 19.0.1.0.2 (was 1.0.2, via marketplace) 2026-05-02 06:52:15 +00:00
bfce835bee Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:52:11 +00:00
a4a0184e3a Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:52:09 +00:00
ba9d23abcc Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:52:05 +00:00
4c811eb71b Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:52:01 +00:00
3183ea091c Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:58 +00:00
cb5a39181f Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:55 +00:00
b80e594535 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:52 +00:00
487da7eb79 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:48 +00:00
55b006145a Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:46 +00:00
272e50202c Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:44 +00:00
c7e08801e3 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:43 +00:00
98c68049eb Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:41 +00:00
4837ebb77b Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:39 +00:00
2c6b22b221 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:37 +00:00
040d040536 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:35 +00:00
1c7b7877a3 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:33 +00:00
25a626bdc8 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:30 +00:00
08af20cc8d Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:27 +00:00
f2d3140e01 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:24 +00:00
da0f9cbcf2 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:22 +00:00
eaa58f6116 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:18 +00:00
74792b00dd Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:16 +00:00
e8d03372a9 Tower: upload om_recurring_payments 19.0.1.0.0 (was 1.0.0, via marketplace) 2026-05-02 06:51:11 +00:00
b05e4dc5c2 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:51:07 +00:00
ed17b07946 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:51:03 +00:00
be48603ea5 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:51:00 +00:00
065927abb9 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:55 +00:00
46e8c1e977 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:51 +00:00
dca8863bbd Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:49 +00:00
55bdcfbdc6 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:46 +00:00
10975b612e Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:42 +00:00
2bea9c692e Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:38 +00:00
29052e9644 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:34 +00:00
3f30b37505 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:31 +00:00
5d363428a2 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:28 +00:00
9a14ce45c9 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:26 +00:00
49198d27cc Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:22 +00:00
2002bd8dc2 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:18 +00:00
7522e64961 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:15 +00:00
7c8e4574e7 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:12 +00:00
c42184c3b2 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:10 +00:00
b7f1e8f324 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:07 +00:00
966efb56c0 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:50:02 +00:00
91a3aac647 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:58 +00:00
ec80e5f007 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:53 +00:00
87df9231e4 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:48 +00:00
1bd64417b8 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:44 +00:00
0a800bf1aa Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:41 +00:00
f0027eee3f Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:40 +00:00
0bca86c256 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:37 +00:00
1e473a1677 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:34 +00:00
1725eafcd9 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:31 +00:00
1ad797d9f0 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:29 +00:00
53be87e93c Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:26 +00:00
1ead3282cf Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:24 +00:00
2c142ccb76 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:23 +00:00
eda163a405 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:19 +00:00
adce085798 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:18 +00:00
495fe47dfa Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:16 +00:00
037d1438e1 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:13 +00:00
54e693e3f1 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:11 +00:00
9d84acdc94 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:07 +00:00
8132d571ac Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:04 +00:00
28567ff9e2 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:49:01 +00:00
ad566ac852 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:48:58 +00:00
e1d95717d6 Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:48:56 +00:00
824265c91a Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:48:54 +00:00
5c5d93f18f Tower: upload om_account_accountant 19.0.1.0.3 (was 1.0.3, via marketplace) 2026-05-02 06:48:51 +00:00
1ed351afcb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:27 +00:00
37d902bbf1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:27 +00:00
4620f1f15d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:26 +00:00
0919e928a2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:25 +00:00
6ccd6f551d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:24 +00:00
d9dbe67b8c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:23 +00:00
00ecdb1eee Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:23 +00:00
a32918ffab Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:22 +00:00
599cc43b87 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:21 +00:00
3e951f4586 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:20 +00:00
179eb40651 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:20 +00:00
1ea0f65bad Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:19 +00:00
37a5973caa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:18 +00:00
3bfd304755 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:17 +00:00
422687b588 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:17 +00:00
e8760f7d0d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:16 +00:00
d7f5f98734 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:15 +00:00
e224f85ce5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:15 +00:00
9793d30a0b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:14 +00:00
7ee8c79c11 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:13 +00:00
c5b1f87197 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:12 +00:00
6d4b838700 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:12 +00:00
32ebb495b2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:11 +00:00
1b0b3816fd Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:10 +00:00
461a147883 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:09 +00:00
96a99e98a6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:09 +00:00
3f1e67983a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:08 +00:00
480a145045 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:07 +00:00
4d6d6e3a0b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:06 +00:00
561595705c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:05 +00:00
6fe848fe50 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:03 +00:00
340836e114 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:02 +00:00
8550a2c003 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:01 +00:00
b9cb1e1d6c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:16:00 +00:00
058d098354 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:59 +00:00
05e11743bc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:58 +00:00
7bd3c34d30 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:57 +00:00
365e38e9bd Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:56 +00:00
da1339981b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:55 +00:00
21e3832c7a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:54 +00:00
cdf57466e0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:53 +00:00
1dd7159ebf Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:52 +00:00
a7aac0a6fc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:51 +00:00
46ddc4e83d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:50 +00:00
f598856a85 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:49 +00:00
e4fab2bcd0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:48 +00:00
2b9c28b17d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:47 +00:00
aa240a5294 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:46 +00:00
3a3f69f769 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:44 +00:00
e1550843f2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:43 +00:00
6e9bd26cc2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:43 +00:00
0274e8118a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:41 +00:00
fbee040fbe Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:40 +00:00
cc39e82b47 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:39 +00:00
cc173aa77f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:38 +00:00
49fd715c87 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:37 +00:00
be38f82271 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:36 +00:00
3d6d13e7f6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:35 +00:00
057fe4b3d5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:34 +00:00
e4b13e539e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:33 +00:00
995b9e65ac Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:32 +00:00
bc01210496 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:31 +00:00
cab4aebd03 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:29 +00:00
7ecc317e64 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:28 +00:00
30d46fbca0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:27 +00:00
9b2180deef Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:26 +00:00
9ad33e290f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:25 +00:00
787efca2b6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:24 +00:00
16ced6a395 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:23 +00:00
773c3cdd6e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:21 +00:00
4629806e3e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:20 +00:00
2129a6d505 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:19 +00:00
bf6096bf45 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:18 +00:00
e8bf47a6a9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:17 +00:00
7eb70e2623 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:16 +00:00
89253bd00a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:15 +00:00
b55ae4fe9d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:14 +00:00
bb68ff0cc5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:13 +00:00
66add92264 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:12 +00:00
2588c66508 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:10 +00:00
5b4a3aab11 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:09 +00:00
88118d9448 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:08 +00:00
d389ec4902 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:07 +00:00
81bc19c9ca Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:06 +00:00
5859333ac1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:04 +00:00
403db72dc7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:03 +00:00
1e058fdb9a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:02 +00:00
8c015d75be Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:15:00 +00:00
1ffc9fe53f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:59 +00:00
ea982c7dc5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:58 +00:00
d6922f99a3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:58 +00:00
a04bf4be8b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:57 +00:00
af2de3d58f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:55 +00:00
485d66cb19 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:55 +00:00
18e3ab1723 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:54 +00:00
9abef0c065 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:53 +00:00
8aff2d5df6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:51 +00:00
926071995c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:51 +00:00
69fc239de1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:49 +00:00
08bfaa13cf Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:48 +00:00
43d06b9ead Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:47 +00:00
100f596f31 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:46 +00:00
6b1e846f86 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:45 +00:00
2a3bd8b770 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:43 +00:00
cad4fdef9e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:43 +00:00
f25a7de844 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:41 +00:00
da3727481e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:40 +00:00
44c80d21ea Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:39 +00:00
0f815cd793 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:38 +00:00
962f5792a3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:37 +00:00
9527852970 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:36 +00:00
b5aef11d8e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:35 +00:00
90e3b774fe Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:33 +00:00
d12ac3d874 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:32 +00:00
f5593b775d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:31 +00:00
64268fad89 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:30 +00:00
9e9b83e383 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:29 +00:00
b0959026a5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:28 +00:00
c94d4b2692 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:26 +00:00
feb7eac8d0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:25 +00:00
deb912fe74 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:24 +00:00
f48188cdc2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:23 +00:00
ef2a582cbb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:22 +00:00
efa56c6aee Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:21 +00:00
3b45b08cd1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:19 +00:00
8ad2d11c45 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:18 +00:00
35b29fa27c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:17 +00:00
8f09fa4305 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:16 +00:00
b5b245876c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:14 +00:00
b16b052934 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:13 +00:00
b069c94742 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:12 +00:00
03e8bb9e34 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:10 +00:00
bf629028b6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:09 +00:00
3395d3b27d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:08 +00:00
e14357c691 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:07 +00:00
5b071517d5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:06 +00:00
83906f95f7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:04 +00:00
164d5e4e27 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:03 +00:00
1575a75869 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:02 +00:00
963b0d0dfa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:14:00 +00:00
f65e31fabc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:58 +00:00
d0bc1094b5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:57 +00:00
e927eea9d2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:56 +00:00
db23098f86 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:55 +00:00
0acea56f70 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:54 +00:00
3b1e6031ed Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:53 +00:00
e600110516 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:52 +00:00
52330d8399 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:50 +00:00
b0cc42047c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:49 +00:00
e94654c113 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:48 +00:00
5ed65f685d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:47 +00:00
a997f2fa4b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:46 +00:00
7ff298551b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:45 +00:00
83dc76f9da Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:44 +00:00
603a8f1c72 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:42 +00:00
2618f5bc15 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:41 +00:00
53ed74871d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:40 +00:00
1c2bbaf08e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:39 +00:00
e1001bc697 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:38 +00:00
5e517adbef Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:37 +00:00
823f4a4078 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:36 +00:00
40caeba39e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:35 +00:00
ee4a4afa94 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:34 +00:00
c7fc09061d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:33 +00:00
3292953c6e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:32 +00:00
0c6035f883 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:30 +00:00
d5dec2ce97 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:29 +00:00
db7810ce15 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:28 +00:00
b2657e9c92 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:27 +00:00
48483a2e25 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:26 +00:00
cc2215e75f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:25 +00:00
baf34a96a9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:23 +00:00
c412cb62b4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:22 +00:00
58abc5c6df Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:21 +00:00
0b69dbf4cc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:20 +00:00
0c44c5f90d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:19 +00:00
c1886be4a6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:18 +00:00
832c6c58e0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:16 +00:00
b918b2d54b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:15 +00:00
731f8ef60c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:14 +00:00
23c946dee2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:13 +00:00
4b70c0b1b8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:11 +00:00
a80a309f6b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:10 +00:00
76b546e0d2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:09 +00:00
e12cbe8a1d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:08 +00:00
53f96e1c12 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:07 +00:00
c6fcc52911 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:05 +00:00
001f689a99 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:04 +00:00
dd63749c5f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:02 +00:00
d0a6a9ba6f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:01 +00:00
3d9224af3c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:13:00 +00:00
2547699573 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:59 +00:00
12d9818809 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:57 +00:00
019c26cbd2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:56 +00:00
a2abda6b07 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:55 +00:00
649d3a13dd Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:54 +00:00
96e8abf5de Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:53 +00:00
2989ad5a81 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:51 +00:00
dc5a6616a4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:50 +00:00
b1e930eafb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:49 +00:00
c1521a6372 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:48 +00:00
269beaf624 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:47 +00:00
13e5e7cde8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:45 +00:00
4bbcae399e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:44 +00:00
14899e48d3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:43 +00:00
0e5a2af28e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:42 +00:00
83277217f9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:40 +00:00
4d226ec086 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:39 +00:00
2d41262428 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:38 +00:00
8dfd64f53a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:37 +00:00
ea73d93e8a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:36 +00:00
1e4e023c1c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:35 +00:00
41ecea74c6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:33 +00:00
ac2ff0476b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:32 +00:00
147c1e5050 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:31 +00:00
2a6c7e3a87 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:30 +00:00
5c2ae7b2c4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:28 +00:00
0fed636461 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:28 +00:00
d1772985ab Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:26 +00:00
697315c47f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:25 +00:00
ae802507a3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:24 +00:00
cb69646f9f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:23 +00:00
91a454851c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:22 +00:00
7eb66dd5a4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:20 +00:00
67db32090b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:19 +00:00
45091483a3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:18 +00:00
b4f0e672b3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:17 +00:00
0603a3d6af Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:16 +00:00
0e403e40f7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:15 +00:00
8e857c5d16 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:12:14 +00:00
27f1d35c41 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:35 +00:00
25ab8eb3a5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:34 +00:00
5e11c33d38 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:33 +00:00
8c450b3275 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:32 +00:00
fc8f57dc1e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:31 +00:00
3eba6d66aa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:30 +00:00
848aae224c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:29 +00:00
5133ee78c9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:27 +00:00
083852cb99 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:26 +00:00
53a7b6a318 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:25 +00:00
ae3a204c7e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:24 +00:00
431b209881 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:23 +00:00
2e2bbff9b0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:22 +00:00
c666b76c2a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:21 +00:00
92f51cc251 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:20 +00:00
a91d88866f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:19 +00:00
f55d2cb472 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:18 +00:00
720276e095 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:17 +00:00
5e3ad11d2a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:15 +00:00
219f706143 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:14 +00:00
9ca4e55cc9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:13 +00:00
254fb1d885 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:12 +00:00
c78fc717af Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:11 +00:00
2512b32b28 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:10 +00:00
b11ad8e295 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:09 +00:00
e9d4d08962 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:08 +00:00
3f9f298838 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:07 +00:00
fe351faefe Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:05 +00:00
e3aa3d8990 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:04 +00:00
5253193596 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:03 +00:00
3001e7c0b2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:01 +00:00
ec436f6e9c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:09:00 +00:00
7872aa608b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:59 +00:00
da801c6eaf Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:57 +00:00
2ebda56c3f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:56 +00:00
a4b2164f7c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:55 +00:00
58bf5a2def Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:54 +00:00
7c33f0b44f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:53 +00:00
bdde4082d7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:52 +00:00
1b90a3e867 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:50 +00:00
d524c5e1d4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:49 +00:00
f01f577792 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:48 +00:00
27ce1c54a5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:47 +00:00
128525d282 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:46 +00:00
32003002ac Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:45 +00:00
94e116c52d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:44 +00:00
c1ba536d03 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:42 +00:00
9b7541ccc3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:41 +00:00
5f3f5baf37 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:40 +00:00
8d12539329 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:39 +00:00
f5719810a4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:38 +00:00
db0cb50deb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:37 +00:00
2b65de6758 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:36 +00:00
030be683b1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:34 +00:00
14d5ad7f29 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:33 +00:00
531ad91474 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:32 +00:00
d4a490c757 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:31 +00:00
9f15d65490 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:30 +00:00
a2a74e8744 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:29 +00:00
c21e46ddeb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:27 +00:00
da07d2e445 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:26 +00:00
6dc553fde9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:25 +00:00
f99799a954 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:24 +00:00
9e23e1317c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:23 +00:00
32d774fcd5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:22 +00:00
e207f08083 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:20 +00:00
47a23fed67 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:19 +00:00
c1a763bddc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:18 +00:00
7d30801033 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:17 +00:00
84e8400cb4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:16 +00:00
3e481ba6ab Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:15 +00:00
e33e0077a1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:13 +00:00
75ba8a98cb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:12 +00:00
0366582790 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:11 +00:00
f689083840 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:10 +00:00
48703f32da Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:09 +00:00
2de2a5d904 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:08 +00:00
a785bff7bc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:07 +00:00
545a23229d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:05 +00:00
1a354517a3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:04 +00:00
d99608dfe0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:03 +00:00
7fd8ff1230 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:02 +00:00
e4f3261e8a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:08:01 +00:00
f76ece3c19 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:59 +00:00
61be87a6a0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:58 +00:00
32ef4a1135 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:57 +00:00
105f6a4b1d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:56 +00:00
76487f4782 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:55 +00:00
d854dde695 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:54 +00:00
0d2ab5ef0f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:53 +00:00
eef53a3aaf Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:07:01 +00:00
5cd06f5379 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:59 +00:00
ba653ff7da Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:58 +00:00
8910346aa4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:58 +00:00
d004eb3298 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:57 +00:00
953d506294 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:55 +00:00
2d1a474823 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:54 +00:00
4aaf162b07 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:53 +00:00
55919440aa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:52 +00:00
def3919e36 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:51 +00:00
23729f436a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:50 +00:00
a498382b27 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:49 +00:00
ef2464809f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:48 +00:00
c5bc923362 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:47 +00:00
b921e79306 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:46 +00:00
7d31a8c951 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:45 +00:00
cbd4217d7d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:44 +00:00
a9a25dbf23 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:43 +00:00
e9d49c9a58 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:42 +00:00
0682aecf37 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:41 +00:00
2637250db2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:40 +00:00
d33628aebd Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:39 +00:00
a80add2939 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:37 +00:00
41de310ee1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:37 +00:00
a6bebfd99c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:35 +00:00
76e4d29876 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:34 +00:00
8ade4a6e05 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:33 +00:00
a6f421a104 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:32 +00:00
8aa7c35368 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:31 +00:00
6c8caf18a8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:30 +00:00
66986764f8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:29 +00:00
fa4f2d6714 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:27 +00:00
8f445713f2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:26 +00:00
40fb8f2fa6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:25 +00:00
33d3610337 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:24 +00:00
569ed5fa45 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:22 +00:00
ffb2695771 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:21 +00:00
0b2dad4a11 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:20 +00:00
f2c0f1dbf1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:19 +00:00
1f4d827be0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:18 +00:00
77237f7463 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:17 +00:00
275e79a729 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:17 +00:00
589c24b30c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:15 +00:00
9f7dc4f18b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:14 +00:00
7243cebeb7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:13 +00:00
968080a32f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:12 +00:00
711ae46103 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:11 +00:00
a2bbff1742 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:10 +00:00
2cfc0fbb9e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:09 +00:00
0f5db71fcc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:08 +00:00
ab3cdca6ac Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:07 +00:00
b6b317cf39 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:06 +00:00
a572280a1b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:05 +00:00
0bba7d8660 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:04 +00:00
b83c259bc9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:03 +00:00
275005173e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:02 +00:00
e894d52fb3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:06:00 +00:00
d93f8decd8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:59 +00:00
d3b0c242da Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:58 +00:00
b198163958 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:57 +00:00
a947ad7169 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:56 +00:00
fb945d1f13 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:55 +00:00
b70d7ffd84 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:54 +00:00
799550985c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:53 +00:00
0507e609d0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:52 +00:00
6e6c57734f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:51 +00:00
3a8001bbf5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:50 +00:00
c5531c9622 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:48 +00:00
5fc1508fbb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:47 +00:00
5b2176f740 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:47 +00:00
8e0f4bedc1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:45 +00:00
d0a6262774 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:44 +00:00
50c3b59309 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:43 +00:00
382b052b20 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:42 +00:00
a942abb5d8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:41 +00:00
8d8980a351 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:40 +00:00
9147825a27 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:39 +00:00
a5bfe360bc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:38 +00:00
9b0de5b906 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:36 +00:00
0a961e60f2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:36 +00:00
b56eef73af Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:35 +00:00
c01bf9817e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:34 +00:00
aad8160ddb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:33 +00:00
01ad9545ef Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:31 +00:00
891226fb3a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:30 +00:00
51df143937 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:29 +00:00
4bd584cc1c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:28 +00:00
2983906b42 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:27 +00:00
9b503f6da4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:26 +00:00
3be92de4da Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:24 +00:00
f2b145fbe3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:23 +00:00
b441195cbf Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:22 +00:00
c680ca48b9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:21 +00:00
3f3f72f1b2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:20 +00:00
d24265e1f7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:19 +00:00
a1debc4b51 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:18 +00:00
3f87848fb0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:16 +00:00
6a5d7bd5c9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:15 +00:00
3e5e94610b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:14 +00:00
8fef2569a6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:13 +00:00
b67e36219b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:12 +00:00
8b9bc53b4b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:11 +00:00
7f0c5bb6d6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:09 +00:00
a624a78e89 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:08 +00:00
2d007bddd6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:07 +00:00
3b0fd1a653 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:06 +00:00
6dfec1ece8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:05 +00:00
f2f9f1356e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:04 +00:00
8013a8466b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:02 +00:00
1ea2132ae1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:05:01 +00:00
f625656478 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:59 +00:00
97c12ce152 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:58 +00:00
8f4d684054 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:57 +00:00
7345062f82 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:56 +00:00
745334a16a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:55 +00:00
b2ede24091 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:54 +00:00
1c8f09b64c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:53 +00:00
c1c520274f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:52 +00:00
d000fead4c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:51 +00:00
0de0a6d5aa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:50 +00:00
0e872ab117 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:49 +00:00
528f191d56 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:48 +00:00
ce4ee639d3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:47 +00:00
f810014cb2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:04:46 +00:00
9d51ac3252 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:49 +00:00
a2028e5a97 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:48 +00:00
e9cadbe823 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:47 +00:00
3f799e5d6f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:46 +00:00
8722d4b1b2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:45 +00:00
e653e447c6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:44 +00:00
88c2a9003a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:43 +00:00
739c87dd5e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:42 +00:00
046eb43f56 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:41 +00:00
8c17569d52 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:40 +00:00
0f995e80a9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:39 +00:00
09696b9c10 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:38 +00:00
b6362d895d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:37 +00:00
fc6d7d9b29 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:36 +00:00
4bc1d5939e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:34 +00:00
fb1ec4b879 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:34 +00:00
d3a3fa3c77 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:33 +00:00
72e36fbf76 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:32 +00:00
558ed2306c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:31 +00:00
0e8fbce3ea Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:30 +00:00
b0cecf4b66 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:29 +00:00
314f12f0d4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:27 +00:00
df0d005724 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:26 +00:00
a487adf187 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:25 +00:00
5e40d1c63a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:24 +00:00
304c5172e6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:23 +00:00
23b7b1e80b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:22 +00:00
5bbab6fab5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:21 +00:00
1585c66a2d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:20 +00:00
f57d262d62 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:19 +00:00
69c4b974ef Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:18 +00:00
214433437e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:17 +00:00
de13dc5909 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:16 +00:00
ad34c1d6eb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:15 +00:00
ac1f5c0c8f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:14 +00:00
475273a71b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:12 +00:00
2f525cb95e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:11 +00:00
9874bd4e21 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:10 +00:00
0212b26047 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:09 +00:00
20da29605b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:08 +00:00
034eab9244 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:07 +00:00
60fc5bcdda Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:06 +00:00
6a80f28c52 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:05 +00:00
13bb0df929 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:04 +00:00
04b4210da2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:03 +00:00
7d03e32f51 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:02 +00:00
4fdbdaedc9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:03:01 +00:00
1cc392bfb2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:59 +00:00
c856fed428 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:58 +00:00
c4a447738e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:57 +00:00
d3b296b587 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:56 +00:00
c678877b59 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:55 +00:00
104f73b7d2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:54 +00:00
135ae7fcfe Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:53 +00:00
9d502807fb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:52 +00:00
c3581e1b69 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:51 +00:00
3ce9111fa7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:50 +00:00
516e985bd0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:49 +00:00
46c0d054a6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:48 +00:00
392984c5e8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:47 +00:00
2bc536263e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:46 +00:00
957fb6f312 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:45 +00:00
4dbea36b0f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:44 +00:00
3c67472a8c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:43 +00:00
f6e22cc063 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:42 +00:00
9b4f874307 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:41 +00:00
a054dc878a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:40 +00:00
48e48ecf03 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:39 +00:00
471c451c21 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:38 +00:00
a88793a6ad Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:37 +00:00
3e50cea188 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:36 +00:00
4da0dfbd51 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:35 +00:00
da5eab2f1f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:34 +00:00
78e1187722 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:32 +00:00
bfda8e2e56 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:31 +00:00
5014234817 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:30 +00:00
a185c34d65 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:29 +00:00
8fc4975a9e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:28 +00:00
7414a836e7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:27 +00:00
122f31f4b6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:26 +00:00
b59d8cee90 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:25 +00:00
1db27642af Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:24 +00:00
6f5be6eae9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:23 +00:00
2a7103cdd3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:22 +00:00
af722d6184 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:20 +00:00
2bf0695e9d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:19 +00:00
faf1b4fff1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:18 +00:00
bd59526535 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:17 +00:00
f404e0ef31 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:16 +00:00
a11b547cdc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:02:15 +00:00
1507d10fe5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:26 +00:00
84cc63a038 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:25 +00:00
cbb5244320 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:25 +00:00
809e33bd93 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:24 +00:00
b8f6c82010 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:23 +00:00
4be3583e7b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:22 +00:00
de10442135 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:21 +00:00
44ec4115ea Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:21 +00:00
eb27193b6e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:20 +00:00
cead209507 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:19 +00:00
f2122a7706 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:19 +00:00
88f970547f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:18 +00:00
fc7bfaa089 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:17 +00:00
234aee88e2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:17 +00:00
33a761413c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:16 +00:00
db76be28e3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:15 +00:00
62a64bcadc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:15 +00:00
3d69d5b814 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:14 +00:00
850d9bb6c5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:13 +00:00
89f02eeda5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:12 +00:00
cba2ad4052 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:12 +00:00
f566b64a4e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:11 +00:00
9e5070e4c7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:10 +00:00
d95c52e27a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:10 +00:00
051e01b1d5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:09 +00:00
4261ec5ed0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:08 +00:00
98bc5f8027 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:08 +00:00
ccdc19f576 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:07 +00:00
84e6416ca1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:06 +00:00
368561c08a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:06 +00:00
3de397f595 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:05 +00:00
700d367666 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:04 +00:00
a384996942 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:04 +00:00
81f0c9411c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:03 +00:00
cbdea4a66a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:02 +00:00
752c1ebe59 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:01 +00:00
f77ab77c0b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:00 +00:00
71d223259b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:01:00 +00:00
a69b413c5b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:59 +00:00
daafc7c705 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:58 +00:00
52b130631c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:57 +00:00
01594ba32b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:57 +00:00
9015195fb9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:56 +00:00
6d07bc4c06 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:55 +00:00
acf12b508b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:55 +00:00
b7a2c4ea2f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:54 +00:00
4399a06c37 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:54 +00:00
5ee5d4f5cd Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:53 +00:00
256c5679c7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:52 +00:00
45bfc361f7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:52 +00:00
0ecbc9213c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:51 +00:00
63dc434824 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:50 +00:00
3e1a94eaed Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:50 +00:00
68ce996d7b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:49 +00:00
89d5846a15 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:48 +00:00
7f76c2d7e7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:47 +00:00
1326d21dfa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:47 +00:00
b37e8a0f7a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:46 +00:00
8f629981ce Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:45 +00:00
beace16323 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:45 +00:00
e19464e2a8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:44 +00:00
008ca2b8a7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:43 +00:00
6e56dc2437 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:43 +00:00
a09d856ddb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:42 +00:00
9a69aa7709 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:41 +00:00
0254cac7fc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:40 +00:00
bc629b414c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:40 +00:00
8f69ab2665 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:39 +00:00
01514c86c9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:39 +00:00
ca1f963db1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:38 +00:00
5fd44a1ae0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:37 +00:00
284e2939f5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:37 +00:00
70d43d34ca Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:36 +00:00
8425e51ca4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:35 +00:00
4994547440 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:34 +00:00
2fbf03f179 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:34 +00:00
2e1a1fbe03 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:33 +00:00
dd5c59c645 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:32 +00:00
3e88c5783b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:32 +00:00
0a63809482 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:31 +00:00
743b002387 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:30 +00:00
79fe584847 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:30 +00:00
63d6a65f65 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:29 +00:00
d20f9916f3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:29 +00:00
285397f44b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:28 +00:00
5d7a1983f9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:27 +00:00
8cb7ce7d65 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:26 +00:00
2b78518c30 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:26 +00:00
b6be57e58f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:25 +00:00
e225ff2780 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:24 +00:00
b98f7b4769 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:24 +00:00
795d6e7d9d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:23 +00:00
100198bd1f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:22 +00:00
23a254a556 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:21 +00:00
12edb222fe Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:21 +00:00
e8ba8fa737 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:20 +00:00
b4322a0037 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:19 +00:00
4ad5be42ac Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:19 +00:00
36edebf085 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:18 +00:00
fed7367831 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:17 +00:00
f2733d5329 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:16 +00:00
bb090554de Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:15 +00:00
42a7238060 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:14 +00:00
f9df23afbd Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:13 +00:00
81e9e5a672 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:12 +00:00
c7d69b60ae Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:11 +00:00
a2f9f9b7c5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:10 +00:00
32431d413c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:09 +00:00
cfa3b92440 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:09 +00:00
d84b02bbd3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:08 +00:00
55c7f73cf0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:07 +00:00
0ca0df7b4d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:06 +00:00
57d989b675 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:05 +00:00
de9e8736a2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:04 +00:00
c404a4666b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:02 +00:00
25707d453f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:01 +00:00
39099edaf2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 15:00:00 +00:00
284678d009 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:59 +00:00
30c49600c4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:59 +00:00
a3dbb7e17b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:58 +00:00
6008b2dc05 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:57 +00:00
829ad64e09 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:56 +00:00
25043fe102 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:56 +00:00
88686ff2c3 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:55 +00:00
12abc05f7d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:54 +00:00
aefa4babca Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:54 +00:00
c62fd5ecfa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:53 +00:00
7eb7f7ea8e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:52 +00:00
d9fe73f32b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:52 +00:00
e7824591a7 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:51 +00:00
b05fc21d36 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:51 +00:00
d93328bf86 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:50 +00:00
f4ccfb22ac Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:49 +00:00
0113a319f4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:48 +00:00
0ee6d65498 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:48 +00:00
86de2d1eb9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:47 +00:00
db3ab398e9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:47 +00:00
cc3bcd9819 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:46 +00:00
7230f7788f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:45 +00:00
bb433c6f46 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:44 +00:00
b3883d85d5 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:44 +00:00
65e8752498 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:43 +00:00
6327aefce2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:42 +00:00
be499f838f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:41 +00:00
5a4e74189a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:41 +00:00
071b72038c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:40 +00:00
43e08cbef1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:39 +00:00
3acbb098bc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:39 +00:00
01ea86d870 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:38 +00:00
022900429a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:37 +00:00
f468d2f4fd Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:37 +00:00
7e8d4e2128 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:36 +00:00
c5bd3349f8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:35 +00:00
1185f5ea8b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:35 +00:00
eeb9acdef1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:34 +00:00
71924556d6 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:33 +00:00
437bf1914f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:33 +00:00
2247a2b2aa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:32 +00:00
142ef0ddd2 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:31 +00:00
a6ef031a85 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:31 +00:00
1201b65d40 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:30 +00:00
0e58a32f10 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:30 +00:00
1ac96b414e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:29 +00:00
1f2b9fbed1 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:28 +00:00
d491d1fc0b Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:28 +00:00
2475691c4e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:27 +00:00
87d4426b3d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:26 +00:00
0ecc6a5a18 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:26 +00:00
844bc6c148 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:25 +00:00
66dfe1c5e4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:24 +00:00
150ecdf120 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:23 +00:00
aad3d4bd50 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:23 +00:00
6cc75e639c Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:22 +00:00
a628c83a90 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:20 +00:00
40dfe0fccc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:19 +00:00
215e5b1677 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:18 +00:00
cd5eda9df9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:17 +00:00
cad8f0a19d Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:16 +00:00
8563621d65 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:15 +00:00
86fce55c97 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:14 +00:00
86d61ed1a0 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:12 +00:00
fbacd499dc Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:11 +00:00
ccb8b3d268 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:10 +00:00
1773ce4444 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:10 +00:00
c2517abff9 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:09 +00:00
e87775affa Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:08 +00:00
cc5c04d5da Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:07 +00:00
a18cbbf49a Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:07 +00:00
ee3e8edfa8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:06 +00:00
79b3210bda Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:05 +00:00
9580b1f29e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:04 +00:00
04a7bbbda8 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:04 +00:00
c6857b850e Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:03 +00:00
48a025302f Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:02 +00:00
38246e90ee Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:01 +00:00
7e1e94d165 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:59:00 +00:00
aa83112e71 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:59 +00:00
044fec0d50 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:58 +00:00
e32318b2ad Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:58 +00:00
e742c77d12 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:57 +00:00
20ed701fec Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:56 +00:00
e1e2481bbb Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:55 +00:00
015696e7c4 Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:54 +00:00
d318a5a9ec Tower: upload laundry_management 19.0.19.0.4 (via marketplace) 2026-05-01 14:58:54 +00:00
d802d63444 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:27:00 +00:00
7decda722d Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:59 +00:00
4a8b911529 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:58 +00:00
435650436c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:57 +00:00
7eb8ce5eba Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:57 +00:00
45ab562a84 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:56 +00:00
1cd28a15b5 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:55 +00:00
be15835a67 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:54 +00:00
0043ea605b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:53 +00:00
a9d9132707 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:53 +00:00
968f881838 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:52 +00:00
6e5863b6f0 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:50 +00:00
f096aad827 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:50 +00:00
75afd665b8 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:49 +00:00
2f8a31ca87 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:48 +00:00
02c94565a6 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:45 +00:00
2eaabfca59 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:44 +00:00
c300658c45 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:43 +00:00
ffd3540da7 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:41 +00:00
af57c49e0c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:40 +00:00
3cb908e43a Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:39 +00:00
b9763d1cab Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:38 +00:00
3081b1727e Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:36 +00:00
795a79e2de Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:36 +00:00
622d469902 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:35 +00:00
0d3d9fd659 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:34 +00:00
0624e3329d Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:34 +00:00
d37effb75e Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:33 +00:00
5d6cbe8712 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:32 +00:00
7da2508ad5 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:32 +00:00
e84b5fc21c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:31 +00:00
302b4e2086 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:30 +00:00
8979652c30 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:29 +00:00
cd5e49cad6 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:28 +00:00
aceab86bd0 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:28 +00:00
ac0562565d Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:27 +00:00
dea9410bff Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:26 +00:00
321300cb96 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:25 +00:00
fe85d4a831 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:25 +00:00
d96900edcd Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:24 +00:00
506f0f11c4 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:23 +00:00
ae32f463b7 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:22 +00:00
4df8ed7aca Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:22 +00:00
4319b86036 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:21 +00:00
af9f7335fe Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:19 +00:00
6dd9422d13 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:16 +00:00
c161cf6911 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:16 +00:00
55a84c282b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:15 +00:00
5ef1cc0aed Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:14 +00:00
c774c5b9ed Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:13 +00:00
03914adbd3 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:13 +00:00
f944dc402e Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:12 +00:00
41a2fb8e87 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:11 +00:00
d98c59977c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:10 +00:00
5635da77ea Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:09 +00:00
f566e6ebc0 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:09 +00:00
7ac4617819 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:08 +00:00
e5a427556a Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:07 +00:00
eadaacc266 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:06 +00:00
94c1637d10 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:05 +00:00
3f9d72c4fe Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:05 +00:00
00bdf183bb Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:04 +00:00
0b5452b9ef Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:03 +00:00
603e4a5a7a Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:02 +00:00
2682634640 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:02 +00:00
5bac29e608 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:01 +00:00
0b34ccfe0b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:26:00 +00:00
4d9163364b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:59 +00:00
64aa4f4cd8 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:59 +00:00
10755bf441 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:58 +00:00
95cb5d6eb5 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:57 +00:00
bd05b6f737 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:57 +00:00
90bfd7cd31 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:56 +00:00
6aebd3a480 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:54 +00:00
fa5a12e8b7 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:52 +00:00
a4f1c765d0 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:52 +00:00
6809c236c3 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:51 +00:00
53dd954217 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:50 +00:00
525f4160a7 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:49 +00:00
f9f462b129 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:49 +00:00
1f55211ecd Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:48 +00:00
db8ff1ab76 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:47 +00:00
98fb9f325e Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:47 +00:00
0811265bc1 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:46 +00:00
4357f1842a Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:45 +00:00
3a54c60b51 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:45 +00:00
c79cf90797 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:44 +00:00
11573d49d4 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:43 +00:00
15fc7bb78b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:42 +00:00
e92df10e2b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:41 +00:00
b49af826f3 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:41 +00:00
a95c2da928 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:40 +00:00
c04db924cc Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:39 +00:00
c2ff38fd10 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:38 +00:00
3b51b7e059 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:38 +00:00
473f603fee Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:37 +00:00
03136df83e Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:36 +00:00
7c3e66de7c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:36 +00:00
50b4c9349a Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:35 +00:00
dab6e99136 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:34 +00:00
2a74c6981c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:34 +00:00
bbf0e0ede6 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:33 +00:00
107b28dd6b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:32 +00:00
45d4562015 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:32 +00:00
0305486fc0 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:31 +00:00
92ce1ad080 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:30 +00:00
465f081a01 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:29 +00:00
a7bfa330d5 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:29 +00:00
c4116b0001 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:28 +00:00
d724678c31 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:26 +00:00
7b6bc7c32c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:26 +00:00
14006bf8b6 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:25 +00:00
16ffcf64c9 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:24 +00:00
16c7dfa4c7 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:23 +00:00
a5bc3df00b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:22 +00:00
c3697aa775 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:21 +00:00
0545a5e27f Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:20 +00:00
d4140f6046 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:20 +00:00
4e83a19734 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:19 +00:00
90d255d5b7 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:18 +00:00
fde08f1a51 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:17 +00:00
367c39cd19 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:16 +00:00
1fe1b890f7 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:16 +00:00
039f0b9679 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:14 +00:00
b59bf760fc Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:13 +00:00
254a5edeb1 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:12 +00:00
e1cd635b2b Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:11 +00:00
fd488f3611 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:11 +00:00
936141a47c Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:10 +00:00
00ad28e7e5 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:09 +00:00
133d01f1bc Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:08 +00:00
2bc961a7f6 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:08 +00:00
2763df5b85 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:07 +00:00
c70fc08d8a Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:06 +00:00
fb315401ed Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:04 +00:00
7426af0136 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:03 +00:00
7d4ce79754 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:02 +00:00
76c842ad49 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:01 +00:00
c5b643b175 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:00 +00:00
7be3e6fc46 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:25:00 +00:00
3768d57ef1 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:24:59 +00:00
79a1fc6302 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:24:58 +00:00
cfae5c1e6e Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:24:57 +00:00
ee86816a05 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:24:57 +00:00
3e5625c49a Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:24:56 +00:00
aad046ad01 Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:24:56 +00:00
8b0ce6857d Tower: upload base_accounting_kit 19.0.2.3.1 (via marketplace) 2026-05-01 14:24:55 +00:00
0a00df7d8f Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:27 +00:00
0b04773828 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:26 +00:00
0bfcfc8e5e Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:26 +00:00
6569b22484 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:25 +00:00
6c99447b98 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:25 +00:00
6c2975aa2c Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:24 +00:00
8a323969dd Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:23 +00:00
6adfee8522 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:22 +00:00
94e5c2bc26 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:21 +00:00
a730c30313 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:21 +00:00
0f9842a4e4 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:19 +00:00
d48cd0eb4f Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:19 +00:00
d051b7eb77 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:18 +00:00
229fb1c9d1 Tower: upload om_account_budget 1.0.1 (via marketplace) 2026-05-01 14:21:17 +00:00
88db33f912 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:16 +00:00
ebe40db835 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:16 +00:00
982454b2f5 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:15 +00:00
9335bc4eda Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:14 +00:00
b7b2b72379 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:14 +00:00
68401a1cf2 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:13 +00:00
01e8d981aa Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:12 +00:00
c6b769d7eb Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:11 +00:00
b0b87268fa Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:11 +00:00
434512a7c0 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:10 +00:00
8fe8aa6bbf Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:09 +00:00
65e232de2b Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:08 +00:00
071bfcf31d Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:08 +00:00
568f9daaf1 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:07 +00:00
127b7cb233 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:06 +00:00
4142021d85 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:05 +00:00
12d115d1fc Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:05 +00:00
7766e1a3a5 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:04 +00:00
76fa65609b Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:03 +00:00
87315fbf30 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:02 +00:00
ed7fe289fc Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:02 +00:00
6540dbff99 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:01 +00:00
60c245b407 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:21:00 +00:00
8ffa385b1f Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:59 +00:00
fd0a5a358a Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:59 +00:00
08dd515517 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:58 +00:00
7cef065bde Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:57 +00:00
7ade9aae03 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:57 +00:00
532efc8a07 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:56 +00:00
10719628e3 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:55 +00:00
b8b2528b12 Tower: upload om_account_daily_reports 1.0.1 (via marketplace) 2026-05-01 14:20:55 +00:00
aa4221b2fe Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:54 +00:00
261c16061f Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:53 +00:00
184c88df09 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:53 +00:00
4072474ee3 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:52 +00:00
65145b21fa Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:51 +00:00
6f4831e613 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:50 +00:00
dc4bf9e012 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:49 +00:00
ef5398f5fb Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:49 +00:00
791818fd64 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:48 +00:00
3b08defcc3 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:48 +00:00
d3cf19c3f4 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:47 +00:00
70f019ae37 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:46 +00:00
630fb5358a Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:45 +00:00
b5722ae417 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:45 +00:00
8cd7052a63 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:44 +00:00
dbe0ea924c Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:43 +00:00
7953e4b50f Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:42 +00:00
4315e14697 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:41 +00:00
6e032ba798 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:41 +00:00
c577f33a97 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:40 +00:00
b0327f442e Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:39 +00:00
fbd39e10d6 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:39 +00:00
b18d61edc4 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:38 +00:00
f20f658a5f Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:37 +00:00
88f06ae627 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:37 +00:00
5f076d37de Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:36 +00:00
054ec9151d Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:35 +00:00
7cbd9ab842 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:35 +00:00
79943927dd Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:34 +00:00
6c9c36663e Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:33 +00:00
6b30fe12d0 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:33 +00:00
9ef85d0aa9 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:32 +00:00
7c3def72d6 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:31 +00:00
481c83dce4 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:30 +00:00
7210ca1ffd Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:30 +00:00
d832e40e73 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:29 +00:00
e3db68618d Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:28 +00:00
a031cefe86 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:28 +00:00
e57e734c40 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:27 +00:00
17b7a9432c Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:26 +00:00
2dadde64d2 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:25 +00:00
2d4e7b3ee2 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:25 +00:00
617e1ddd42 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:24 +00:00
926206c4a7 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:23 +00:00
8b6041cd3d Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:22 +00:00
593267811f Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:21 +00:00
9728383428 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:21 +00:00
194babdf41 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:20 +00:00
6f78e2e524 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:19 +00:00
b04f780820 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:18 +00:00
fd396fe299 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:18 +00:00
0670fe453d Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:17 +00:00
fbfa62ad9a Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:16 +00:00
f4d652059c Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:16 +00:00
72f3aa1ab6 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:15 +00:00
13f6bc6f40 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:14 +00:00
8408279160 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:13 +00:00
ef666a5685 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:13 +00:00
0958b0a64c Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:12 +00:00
d238e31685 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:11 +00:00
1e5abc888e Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:10 +00:00
96a6b7bb1f Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:10 +00:00
d4f016e8a0 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:09 +00:00
99f0c26c35 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:08 +00:00
06d20d0ff0 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:08 +00:00
faac65cddb Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:07 +00:00
cbc4503466 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:06 +00:00
cf972bf291 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:05 +00:00
ebaf474567 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:05 +00:00
bb7f341d77 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:03 +00:00
94189882f7 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:02 +00:00
50a789be28 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:02 +00:00
782012f925 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:01 +00:00
b5fe6ea753 Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:20:00 +00:00
f212637b6a Tower: upload accounting_pdf_reports 1.0.2 (via marketplace) 2026-05-01 14:19:59 +00:00
0957153883 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:58 +00:00
4ddb63b195 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:58 +00:00
8d07a37483 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:57 +00:00
6eed23c19a Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:56 +00:00
fd961a3017 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:55 +00:00
9535a8ad6e Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:55 +00:00
fd0e041e2b Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:54 +00:00
5732bcb409 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:53 +00:00
2c0871abd1 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:53 +00:00
37ffda207c Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:52 +00:00
369070c0fd Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:51 +00:00
822fbaad04 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:51 +00:00
f504f7cc7a Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:50 +00:00
bdaef9f2e7 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:50 +00:00
5ec6423f44 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:49 +00:00
d7e283d902 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:48 +00:00
57ced67409 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:48 +00:00
13723c9273 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:47 +00:00
3be0ea0f7b Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:46 +00:00
ca13d7d3f4 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:46 +00:00
be4975f356 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:45 +00:00
cb006a7a44 Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:44 +00:00
a9dc3dcffa Tower: upload om_recurring_payments 1.0.0 (via marketplace) 2026-05-01 14:19:44 +00:00
885bb255ae Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:43 +00:00
fb7d82d2f8 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:42 +00:00
b35236833f Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:42 +00:00
188e732c27 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:41 +00:00
85a037b7b0 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:40 +00:00
6b4864d8f4 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:40 +00:00
c345f88fb7 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:39 +00:00
dd601d2041 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:38 +00:00
d528c4e8b0 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:37 +00:00
37e7e78579 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:37 +00:00
50ffe3acbf Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:36 +00:00
fe054eb766 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:36 +00:00
d726ee0742 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:35 +00:00
63a2854905 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:34 +00:00
94c47c1d64 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:33 +00:00
99decef40e Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:33 +00:00
2144d6510b Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:32 +00:00
3bc963d086 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:31 +00:00
b0208802cb Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:30 +00:00
7c70894890 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:30 +00:00
b34d911271 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:29 +00:00
afadba60cd Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:28 +00:00
dc9bae18b5 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:28 +00:00
a9da9289b1 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:27 +00:00
9ffa32624c Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:26 +00:00
0028b496e1 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:26 +00:00
11d2704508 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:25 +00:00
f2f9e89a95 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:24 +00:00
cbf21b7ab8 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:23 +00:00
73c3e87596 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:23 +00:00
4597627bbf Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:22 +00:00
2a7f8d8700 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:21 +00:00
976dbf28ec Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:20 +00:00
5be3f70d33 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:20 +00:00
32afc8987c Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:19 +00:00
36eccf478e Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:18 +00:00
afe157d626 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:17 +00:00
c1946aed03 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:17 +00:00
c4243349e5 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:16 +00:00
50f5ca329f Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:16 +00:00
94acef514f Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:15 +00:00
d64353f256 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:14 +00:00
2c6e58c58b Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:13 +00:00
222c0cadf0 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:13 +00:00
35e695d9c3 Tower: upload om_account_accountant 1.0.3 (via marketplace) 2026-05-01 14:19:12 +00:00
b2efe86375 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:35 +00:00
9d9b59c5d3 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:34 +00:00
e8ffe910ee Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:34 +00:00
727fc0e844 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:33 +00:00
f7982d1a0e Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:32 +00:00
ee132a00a2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:31 +00:00
1861912af3 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:31 +00:00
b86139b373 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:30 +00:00
b8c54e3f8e Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:29 +00:00
1b57e46bb1 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:28 +00:00
dd948951db Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:27 +00:00
3bd6e77fa2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:27 +00:00
193600db34 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:26 +00:00
3d2024ab9f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:25 +00:00
2c163771d4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:24 +00:00
26f24c3022 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:23 +00:00
f61e5f7e77 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:23 +00:00
3391a4c864 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:21 +00:00
48ad67fc95 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:21 +00:00
cb0fcf1b06 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:20 +00:00
2e42407d45 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:19 +00:00
695188ae0f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:18 +00:00
9df8e641fd Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:18 +00:00
20806189b9 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:17 +00:00
e85bcbd0a1 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:16 +00:00
a9ab2991d7 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:15 +00:00
9dca096734 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:14 +00:00
657f3e4fb4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:13 +00:00
80bbd23379 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:12 +00:00
50ce4f41a2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:11 +00:00
d57b15f9dd Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:11 +00:00
db8cd58f31 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:10 +00:00
f0745cda51 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:09 +00:00
6752ab966e Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:09 +00:00
8ddc02a7c8 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:08 +00:00
db033c9d8b Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:07 +00:00
d97e5cecb2 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:06 +00:00
46ab5fa005 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:06 +00:00
147f237c6f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:05 +00:00
a488d64cb3 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:04 +00:00
6b5ccf1604 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:03 +00:00
516e1a3bb4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:03 +00:00
4c42f51869 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:02 +00:00
9d8cbe6868 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:01 +00:00
23dace1045 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:58:00 +00:00
c2cb157124 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:59 +00:00
070f8187f1 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:58 +00:00
b4f4024174 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:58 +00:00
91e6814c2f Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:57 +00:00
ef39e07904 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:56 +00:00
71d4b51e09 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:55 +00:00
1a83db992c Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:55 +00:00
76ce02f7fa Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:54 +00:00
4de59a7674 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:53 +00:00
dc0417b263 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:52 +00:00
5684de0fbc Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:51 +00:00
3fae6c3b2d Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:51 +00:00
9a89e53ec4 Tower: upload bi_print_journal_entries 19.0.0.0 (via marketplace) 2026-05-01 13:57:50 +00:00
536 changed files with 74203 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
====================================
Odoo 19 Accounting Financial Reports
====================================
This Module will provide all the financial reports for odoo 19
community edition
Installation
============
To install this module, you need to:
Download the module and add it to your Odoo addons folder. Afterward, log on to
your Odoo server and go to the Apps menu. Trigger the debug mode and update the
list by clicking on the "Update Apps List" link. Now install the module by
clicking on the install button.
Upgrade
============
To upgrade this module, you need to:
Download the module and add it to your Odoo addons folder. Restart the server
and log on to your Odoo server. Select the Apps menu and upgrade the module by
clicking on the upgrade button.
Configuration
=============
There is Nothing to Configure
Credits
=======
Contributors
------------
* Odoo Mates <odoomates@gmail.com>
Author & Maintainer
-------------------
This module is maintained by the Odoo Mates

View File

@@ -0,0 +1,7 @@
from . import wizard
from . import models
from . import report
def _pre_init_clean_m2m_models(env):
env.cr.execute("""DROP TABLE IF EXISTS account_journal_account_report_partner_ledger_rel""")

View File

@@ -0,0 +1,45 @@
{
'name': 'Odoo 19 Accounting Financial Reports',
'version': '19.0.1.0.2', # __odoosky_original_version__: '1.0.2'
'category': 'Invoicing Management',
'description': 'Accounting Reports For Odoo 19, Accounting Financial Reports, '
'Odoo 19 Financial Reports',
'summary': 'Accounting Reports For Odoo 19',
'sequence': '1',
'author': 'Odoo Mates, Odoo SA',
'license': 'LGPL-3',
'company': 'Odoo Mates',
'maintainer': 'Odoo Mates',
'support': 'odoomates@gmail.com',
'website': 'https://www.youtube.com/watch?v=yA4NLwOLZms',
'depends': ['account'],
'live_test_url': 'https://www.youtube.com/watch?v=yA4NLwOLZms',
'data': [
'security/ir.model.access.csv',
'data/account_account_type.xml',
'views/menu.xml',
'views/ledger_menu.xml',
'views/financial_report.xml',
'views/settings.xml',
'wizard/account_report_common_view.xml',
'wizard/partner_ledger.xml',
'wizard/general_ledger.xml',
'wizard/trial_balance.xml',
'wizard/balance_sheet.xml',
'wizard/profit_and_loss.xml',
'wizard/tax_report.xml',
'wizard/aged_partner.xml',
'wizard/journal_audit.xml',
'report/report.xml',
'report/report_partner_ledger.xml',
'report/report_general_ledger.xml',
'report/report_trial_balance.xml',
'report/report_financial.xml',
'report/report_tax.xml',
'report/report_aged_partner.xml',
'report/report_journal_audit.xml',
'report/report_journal_entries.xml',
],
'pre_init_hook': '_pre_init_clean_m2m_models',
'images': ['static/description/banner.gif'],
}

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record model="account.account.type" id="data_account_type_receivable">
<field name="name">Receivable</field>
<field name="type">asset_receivable</field>
</record>
<record model="account.account.type" id="data_account_type_payable">
<field name="name">Payable</field>
<field name="type">liability_payable</field>
</record>
<record model="account.account.type" id="data_account_type_liquidity">
<field name="name">Bank and Cash</field>
<field name="type">asset_cash</field>
</record>
<record model="account.account.type" id="data_account_type_credit_card">
<field name="name">Credit Card</field>
<field name="type">liability_credit_card</field>
</record>
<record model="account.account.type" id="data_account_type_current_assets">
<field name="name">Current Assets</field>
<field name="type">asset_current</field>
</record>
<record model="account.account.type" id="data_account_type_non_current_assets">
<field name="name">Non-current Assets</field>
<field name="type">asset_non_current</field>
</record>
<record model="account.account.type" id="data_account_type_prepayments">
<field name="name">Prepayments</field>
<field name="type">asset_prepayments</field>
</record>
<record model="account.account.type" id="data_account_type_fixed_assets">
<field name="name">Fixed Assets</field>
<field name="type">asset_fixed</field>
</record>
<record model="account.account.type" id="data_account_type_current_liabilities">
<field name="name">Current Liabilities</field>
<field name="type">liability_current</field>
</record>
<record model="account.account.type" id="data_account_type_non_current_liabilities">
<field name="name">Non-current Liabilities</field>
<field name="type">liability_non_current</field>
</record>
<record model="account.account.type" id="data_account_type_equity">
<field name="name">Equity</field>
<field name="type">equity</field>
</record>
<record model="account.account.type" id="data_unaffected_earnings">
<field name="name">Current Year Earnings</field>
<field name="type">equity_unaffected</field>
</record>
<record model="account.account.type" id="data_account_type_revenue">
<field name="name">Income</field>
<field name="type">income</field>
</record>
<record model="account.account.type" id="data_account_type_other_income">
<field name="name">Other Income</field>
<field name="type">income_other</field>
</record>
<record model="account.account.type" id="data_account_type_expenses">
<field name="name">Expenses</field>
<field name="type">expense</field>
</record>
<record model="account.account.type" id="data_account_type_depreciation">
<field name="name">Depreciation</field>
<field name="type">expense_depreciation</field>
</record>
<record model="account.account.type" id="data_account_type_direct_costs">
<field name="name">Cost of Revenue</field>
<field name="type">expense_direct_cost</field>
</record>
<record model="account.account.type" id="data_account_off_sheet">
<field name="name">Off-Balance Sheet</field>
<field name="type">off_balance</field>
</record>
</data>
</odoo>

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

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

@@ -0,0 +1,3 @@
from . import account_account_type
from . import account_financial_report
from . import account_move_line

View File

@@ -0,0 +1,34 @@
from odoo import api, models, fields
class AccountAccountType(models.Model):
_name = "account.account.type"
_description = "Account Account Type"
name = fields.Char('Name', required=True, translate=True)
type = fields.Selection(
selection=[
("asset_receivable", "Receivable"),
("asset_cash", "Bank and Cash"),
("asset_current", "Current Assets"),
("asset_non_current", "Non-current Assets"),
("asset_prepayments", "Prepayments"),
("asset_fixed", "Fixed Assets"),
("liability_payable", "Payable"),
("liability_credit_card", "Credit Card"),
("liability_current", "Current Liabilities"),
("liability_non_current", "Non-current Liabilities"),
("equity", "Equity"),
("equity_unaffected", "Current Year Earnings"),
("income", "Income"),
("income_other", "Other Income"),
("expense", "Expenses"),
("expense_depreciation", "Depreciation"),
("expense_direct_cost", "Cost of Revenue"),
("off_balance", "Off-Balance Sheet"),
],
string="Type",
help="These types are defined according to your country. The type contains more information " \
"about the account and its specificities."
)

View File

@@ -0,0 +1,74 @@
from odoo import api, models, fields
class AccountFinancialReport(models.Model):
_name = "account.financial.report"
_description = "Account Report"
@api.depends('parent_id', 'parent_id.level')
def _get_level(self):
'''Returns a dictionary with key=the ID of a record and value = the level of this
record in the tree structure.'''
for report in self:
level = 0
if report.parent_id:
level = report.parent_id.level + 1
report.level = level
def _get_children_by_order(self):
res = self
children = self.search([('parent_id', 'in', self.ids)], order='sequence ASC')
if children:
for child in children:
res += child._get_children_by_order()
return res
name = fields.Char('Report Name', required=True, translate=True)
parent_id = fields.Many2one('account.financial.report', 'Parent')
children_ids = fields.One2many('account.financial.report', 'parent_id', 'Account Report')
sequence = fields.Integer('Sequence')
level = fields.Integer(compute='_get_level', string='Level', store=True, recursive=True)
type = fields.Selection([
('sum', 'View'),
('accounts', 'Accounts'),
('account_type', 'Account Type'),
('account_report', 'Report Value'),
], 'Type', default='sum')
account_ids = fields.Many2many(
'account.account', 'account_account_financial_report',
'report_line_id', 'account_id', 'Accounts'
)
account_report_id = fields.Many2one('account.financial.report', 'Report Value')
account_type_ids = fields.Many2many(
'account.account.type', 'account_account_financial_report_type',
'report_id', 'account_type_id', 'Account Types'
)
report_domain = fields.Char(string="Report Domain")
sign = fields.Selection(
[('-1', 'Reverse balance sign'), ('1', 'Preserve balance sign')], 'Sign on Reports',
required=True, default='1',
help='For accounts that are typically more debited than credited and that you would '
'like to print as negative amounts in your reports, you should reverse the sign '
'of the balance; e.g.: Expense account. The same applies for accounts that are '
'typically more credited than debited and that you would like to print as positive '
'amounts in your reports; e.g.: Income account.'
)
display_detail = fields.Selection([
('no_detail', 'No detail'),
('detail_flat', 'Display children flat'),
('detail_with_hierarchy', 'Display children with hierarchy')
], 'Display details', default='detail_flat')
style_overwrite = fields.Selection([
('0', 'Automatic formatting'),
('1', 'Main Title 1 (bold, underlined)'),
('2', 'Title 2 (bold)'),
('3', 'Title 3 (bold, smaller)'),
('4', 'Normal Text'),
('5', 'Italic Text (smaller)'),
('6', 'Smallest Text'),
], 'Financial Report Style', default='0',
help="You can set up here the format you want this record to be displayed. "
"If you leave the automatic formatting, it will be computed based on the "
"financial reports hierarchy (auto-computed field 'level').")
children_ids = fields.One2many('account.financial.report', 'parent_id', string='Children')

View File

@@ -0,0 +1,117 @@
import ast
from odoo.osv import expression
from odoo import api, models, fields
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
@api.model
def _where_calc(self, domain, active_test=True):
"""Computes the WHERE clause needed to implement an OpenERP domain.
:param list domain: the domain to compute
:param bool active_test: whether the default filtering of records with
``active`` field set to ``False`` should be applied.
:return: the query expressing the given domain as provided in domain
:rtype: Query
"""
# if the object has an active field ('active', 'x_active'), filter out all
# inactive records unless they were explicitly asked for
if self._active_name and active_test and self.env.context.get('active_test', True):
# the item[0] trick below works for domain items and '&'/'|'/'!'
# operators too
if not any(item[0] == self._active_name for item in domain):
domain = [(self._active_name, '=', 1)] + domain
if domain:
return expression.expression(domain, self).query
else:
return Query(self.env, self._table, self._table_sql)
@api.model
def _apply_ir_rules(self, query, mode='read'):
"""Add what's missing in ``query`` to implement all appropriate ir.rules
(using the ``model_name``'s rules or the current model's rules if ``model_name`` is None)
:param query: the current query object
"""
if self.env.su:
return
# apply main rules on the object
Rule = self.env['ir.rule']
domain = Rule._compute_domain(self._name, mode)
if domain:
expression.expression(domain, self.sudo(), self._table, query)
@api.model
def _query_get(self, domain=None):
self.check_access('read')
context = dict(self.env.context or {})
domain = domain or []
if not isinstance(domain, (list, tuple)):
domain = ast.literal_eval(domain)
date_field = 'date'
if context.get('aged_balance'):
date_field = 'date_maturity'
if context.get('date_to'):
domain += [(date_field, '<=', context['date_to'])]
if context.get('date_from'):
if not context.get('strict_range'):
domain += ['|', (date_field, '>=', context['date_from']), ('account_id.include_initial_balance', '=', True)]
elif context.get('initial_bal'):
domain += [(date_field, '<', context['date_from'])]
else:
domain += [(date_field, '>=', context['date_from'])]
if context.get('journal_ids'):
domain += [('journal_id', 'in', context['journal_ids'])]
state = context.get('state')
if state and state.lower() != 'all':
domain += [('parent_state', '=', state)]
if context.get('company_id'):
domain += [('company_id', '=', context['company_id'])]
elif context.get('allowed_company_ids'):
domain += [('company_id', 'in', self.env.companies.ids)]
else:
domain += [('company_id', '=', self.env.company.id)]
if context.get('reconcile_date'):
domain += ['|', ('reconciled', '=', False), '|', ('matched_debit_ids.max_date', '>', context['reconcile_date']), ('matched_credit_ids.max_date', '>', context['reconcile_date'])]
if context.get('account_tag_ids'):
domain += [('account_id.tag_ids', 'in', context['account_tag_ids'].ids)]
if context.get('account_ids'):
domain += [('account_id', 'in', context['account_ids'].ids)]
if context.get('analytic_tag_ids'):
domain += [('analytic_tag_ids', 'in', context['analytic_tag_ids'].ids)]
if context.get('analytic_account_ids'):
domain += [('analytic_distribution', 'in', context['analytic_account_ids'].ids)]
if context.get('partner_ids'):
domain += [('partner_id', 'in', context['partner_ids'].ids)]
if context.get('partner_categories'):
domain += [('partner_id.category_id', 'in', context['partner_categories'].ids)]
where_clause = ""
where_clause_params = []
tables = ''
if domain:
domain.append(('display_type', 'not in', ('line_section', 'line_note')))
domain.append(('parent_state', '!=', 'cancel'))
query = self._where_calc(domain)
self._apply_ir_rules(query)
from_string, from_params = query.from_clause
where_string, where_params = query.where_clause
tables, where_clause, where_clause_params = from_string, where_string, from_params + where_params
return tables, where_clause, where_clause_params

View File

@@ -0,0 +1,12 @@
from . import report_partner_ledger
from . import report_general_ledger
from . import report_trial_balance
from . import report_tax
from . import report_aged_partner
from . import report_journal
from . import report_financial

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_report_general_ledger" model="ir.actions.report">
<field name="name">General Ledger</field>
<field name="model">account.report.general.ledger</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_general_ledger</field>
<field name="report_file">accounting_pdf_reports.report_general_ledger</field>
</record>
<record id="action_report_partnerledger" model="ir.actions.report">
<field name="name">Partner Ledger</field>
<field name="model">account.report.partner.ledger</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_partnerledger</field>
<field name="report_file">accounting_pdf_reports.report_partnerledger</field>
</record>
<record id="action_report_trial_balance" model="ir.actions.report">
<field name="name">Trial Balance</field>
<field name="model">account.balance.report</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_trialbalance</field>
<field name="report_file">accounting_pdf_reports.report_trialbalance</field>
</record>
<record id="action_report_financial" model="ir.actions.report">
<field name="name">Financial Report</field>
<field name="model">account.financial.report</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_financial</field>
<field name="report_file">accounting_pdf_reports.report_financial</field>
</record>
<record id="action_report_account_tax" model="ir.actions.report">
<field name="name">Tax Report</field>
<field name="model">account.tax.report.wizard</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_tax</field>
<field name="report_file">accounting_pdf_reports.report_tax</field>
</record>
<record id="action_report_aged_partner_balance" model="ir.actions.report">
<field name="name">Aged Partner Balance</field>
<field name="model">res.partner</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_agedpartnerbalance</field>
<field name="report_file">accounting_pdf_reports.report_agedpartnerbalance</field>
</record>
<record id="action_report_journal" model="ir.actions.report">
<field name="name">Journals Audit</field>
<field name="model">account.common.journal.report</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_journal</field>
<field name="report_file">accounting_pdf_reports.report_journal</field>
</record>
<record id="action_report_journal_entries" model="ir.actions.report">
<field name="name">Journals Entries</field>
<field name="model">account.move</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">accounting_pdf_reports.report_journal_entries</field>
<field name="report_file">accounting_pdf_reports.report_journal_entries</field>
<field name="binding_model_id" ref="account.model_account_move"/>
<field name="binding_type">report</field>
</record>
</odoo>

View File

@@ -0,0 +1,263 @@
import time
from odoo import api, models, fields, _
from odoo.exceptions import UserError
from odoo.tools import float_is_zero
from datetime import datetime
from dateutil.relativedelta import relativedelta
class ReportAgedPartnerBalance(models.AbstractModel):
_name = 'report.accounting_pdf_reports.report_agedpartnerbalance'
_description = 'Aged Partner Balance Report'
def _get_partner_move_lines(self, account_type, partner_ids,
date_from, target_move, period_length):
# This method can receive the context key 'include_nullified_amount' {Boolean}
# Do an invoice and a payment and unreconcile. The amount will be nullified
# By default, the partner wouldn't appear in this report.
# The context key allow it to appear
# In case of a period_length of 30 days as of 2019-02-08, we want the following periods:
# Name Stop Start
# 1 - 30 : 2019-02-07 - 2019-01-09
# 31 - 60 : 2019-01-08 - 2018-12-10
# 61 - 90 : 2018-12-09 - 2018-11-10
# 91 - 120 : 2018-11-09 - 2018-10-11
# +120 : 2018-10-10
periods = {}
start = datetime.strptime(str(date_from), "%Y-%m-%d")
date_from = datetime.strptime(str(date_from), "%Y-%m-%d").date()
for i in range(5)[::-1]:
stop = start - relativedelta(days=period_length)
period_name = str((5-(i+1)) * period_length + 1) + '-' + str((5-i) * period_length)
period_stop = (start - relativedelta(days=1)).strftime('%Y-%m-%d')
if i == 0:
period_name = '+' + str(4 * period_length)
periods[str(i)] = {
'name': period_name,
'stop': period_stop,
'start': (i!=0 and stop.strftime('%Y-%m-%d') or False),
}
start = stop
res = []
total = []
cr = self.env.cr
user_company = self.env.user.company_id
user_currency = user_company.currency_id
company_ids = self.env.context.get('company_ids') or [user_company.id]
move_state = ['draft', 'posted']
date = self.env.context.get('date') or fields.Date.today()
company = self.env['res.company'].browse(self.env.context.get('company_id')) or self.env.company
if target_move == 'posted':
move_state = ['posted']
arg_list = (tuple(move_state), tuple(account_type))
reconciliation_clause = '(l.reconciled IS FALSE)'
cr.execute('SELECT debit_move_id, credit_move_id FROM account_partial_reconcile where max_date > %s', (date_from,))
reconciled_after_date = []
for row in cr.fetchall():
reconciled_after_date += [row[0], row[1]]
if reconciled_after_date:
reconciliation_clause = '(l.reconciled IS FALSE OR l.id IN %s)'
arg_list += (tuple(reconciled_after_date),)
arg_list += (date_from, tuple(company_ids))
query = '''
SELECT DISTINCT l.partner_id, UPPER(res_partner.name)
FROM account_move_line AS l left join res_partner on l.partner_id = res_partner.id, account_account, account_move am
WHERE (l.account_id = account_account.id)
AND (l.move_id = am.id)
AND (am.state IN %s)
AND (account_account.account_type IN %s)
AND ''' + reconciliation_clause + '''
AND (l.date <= %s)
AND l.company_id IN %s
ORDER BY UPPER(res_partner.name)'''
cr.execute(query, arg_list)
partners = cr.dictfetchall()
# put a total of 0
for i in range(7):
total.append(0)
# Build a string like (1,2,3) for easy use in SQL query
if not partner_ids:
partner_ids = [partner['partner_id'] for partner in partners if partner['partner_id']]
lines = dict((partner['partner_id'] or False, []) for partner in partners)
if not partner_ids:
return [], [], {}
# This dictionary will store the not due amount of all partners
undue_amounts = {}
query = '''SELECT l.id
FROM account_move_line AS l, account_account, account_move am
WHERE (l.account_id = account_account.id) AND (l.move_id = am.id)
AND (am.state IN %s)
AND (account_account.account_type IN %s)
AND (COALESCE(l.date_maturity,l.date) >= %s)\
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
AND (l.date <= %s)
AND l.company_id IN %s'''
cr.execute(query, (tuple(move_state), tuple(account_type), date_from,
tuple(partner_ids), date_from, tuple(company_ids)))
aml_ids = cr.fetchall()
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
for line in self.env['account.move.line'].browse(aml_ids):
partner_id = line.partner_id.id or False
if partner_id not in undue_amounts:
undue_amounts[partner_id] = 0.0
line_amount = line.company_id.currency_id._convert(line.balance,
user_currency,
company, date)
if user_currency.is_zero(line_amount):
continue
for partial_line in line.matched_debit_ids:
if partial_line.max_date <= date_from:
line_currency = partial_line.company_id.currency_id
line_amount += line_currency._convert(partial_line.amount,
user_currency,
company, date)
for partial_line in line.matched_credit_ids:
if partial_line.max_date <= date_from:
line_currency = partial_line.company_id.currency_id
line_amount -= line_currency._convert(partial_line.amount,
user_currency,
company, date)
if not self.env.user.company_id.currency_id.is_zero(line_amount):
undue_amounts[partner_id] += line_amount
lines[partner_id].append({
'line': line,
'amount': line_amount,
'period': 6,
})
# Use one query per period and store results in history (a list variable)
# Each history will contain: history[1] = {'<partner_id>': <partner_debit-credit>}
history = []
for i in range(5):
args_list = (tuple(move_state), tuple(account_type), tuple(partner_ids),)
dates_query = '(COALESCE(l.date_maturity,l.date)'
if periods[str(i)]['start'] and periods[str(i)]['stop']:
dates_query += ' BETWEEN %s AND %s)'
args_list += (periods[str(i)]['start'], periods[str(i)]['stop'])
elif periods[str(i)]['start']:
dates_query += ' >= %s)'
args_list += (periods[str(i)]['start'],)
else:
dates_query += ' <= %s)'
args_list += (periods[str(i)]['stop'],)
args_list += (date_from, tuple(company_ids))
query = '''SELECT l.id
FROM account_move_line AS l, account_account, account_move am
WHERE (l.account_id = account_account.id) AND (l.move_id = am.id)
AND (am.state IN %s)
AND (account_account.account_type IN %s)
AND ((l.partner_id IN %s) OR (l.partner_id IS NULL))
AND ''' + dates_query + '''
AND (l.date <= %s)
AND l.company_id IN %s'''
cr.execute(query, args_list)
partners_amount = {}
aml_ids = cr.fetchall()
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
for line in self.env['account.move.line'].browse(aml_ids):
partner_id = line.partner_id.id or False
if partner_id not in partners_amount:
partners_amount[partner_id] = 0.0
line_currency_id = line.company_id.currency_id
line_amount = line_currency_id._convert(line.balance, user_currency, company, date)
if user_currency.is_zero(line_amount):
continue
for partial_line in line.matched_debit_ids:
if partial_line.max_date <= date_from:
line_currency_id = partial_line.company_id.currency_id
line_amount += line_currency_id._convert(
partial_line.amount, user_currency, company, date)
for partial_line in line.matched_credit_ids:
if partial_line.max_date <= date_from:
line_currency_id = partial_line.company_id.currency_id
line_amount -= line_currency_id._convert(
partial_line.amount, user_currency, company, date)
if not self.env.user.company_id.currency_id.is_zero(line_amount):
partners_amount[partner_id] += line_amount
lines[partner_id].append({
'line': line,
'amount': line_amount,
'period': i + 1,
})
history.append(partners_amount)
for partner in partners:
if partner['partner_id'] is None:
partner['partner_id'] = False
at_least_one_amount = False
values = {}
undue_amt = 0.0
if partner['partner_id'] in undue_amounts: # Making sure this partner actually was found by the query
undue_amt = undue_amounts[partner['partner_id']]
total[6] = total[6] + undue_amt
values['direction'] = undue_amt
if not float_is_zero(values['direction'], precision_rounding=self.env.user.company_id.currency_id.rounding):
at_least_one_amount = True
for i in range(5):
during = False
if partner['partner_id'] in history[i]:
during = [history[i][partner['partner_id']]]
# Adding counter
total[(i)] = total[(i)] + (during and during[0] or 0)
values[str(i)] = during and during[0] or 0.0
if not float_is_zero(values[str(i)],
precision_rounding=self.env.user.company_id.currency_id.rounding):
at_least_one_amount = True
values['total'] = sum([values['direction']] + [values[str(i)] for i in range(5)])
## Add for total
total[(i + 1)] += values['total']
values['partner_id'] = partner['partner_id']
if partner['partner_id']:
browsed_partner = self.env['res.partner'].browse(partner['partner_id'])
values['name'] = browsed_partner.name and len(
browsed_partner.name) >= 45 and browsed_partner.name[
0:40] + '...' or browsed_partner.name
values['trust'] = browsed_partner.trust
else:
values['name'] = _('Unknown Partner')
values['trust'] = False
if at_least_one_amount or (self.env.context.get('include_nullified_amount') and lines[partner['partner_id']]):
res.append(values)
return res, total, lines
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model') or not self.env.context.get('active_id'):
raise UserError(_("Form content is missing, this report cannot be printed."))
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_id'))
target_move = data['form'].get('target_move', 'all')
date_from = data['form'].get('date_from', time.strftime('%Y-%m-%d'))
if data['form']['result_selection'] == 'customer':
account_type = ['asset_receivable']
elif data['form']['result_selection'] == 'supplier':
account_type = ['liability_payable']
else:
account_type = ['asset_receivable', 'liability_payable']
partner_ids = data['form']['partner_ids']
movelines, total, dummy = self._get_partner_move_lines(
account_type, partner_ids, date_from, target_move, data['form']['period_length']
)
return {
'doc_ids': self.ids,
'doc_model': model,
'data': data['form'],
'docs': docs,
'time': time,
'get_partner_lines': movelines,
'get_direction': total,
}

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_agedpartnerbalance">
<t t-call="web.html_container">
<t t-set="data_report_margin_top" t-value="12"/>
<t t-set="data_report_header_spacing" t-value="9"/>
<t t-set="data_report_dpi" t-value="110"/>
<t t-call="web.internal_layout">
<div class="page">
<h2>Aged Partner Balance</h2>
<div class="row mt32">
<div class="col-3">
<strong>Start Date:</strong>
<p t-esc="data['date_from']"/>
</div>
<div class="col-3">
<strong>Period Length (days)</strong>
<p t-esc="data['period_length']"/>
</div>
</div>
<div class="row mb32">
<div class="col-3">
<strong>Partner's:</strong>
<p>
<span t-if="data['result_selection'] == 'customer'">Receivable Accounts</span>
<span t-if="data['result_selection'] == 'supplier'">Payable Accounts</span>
<span t-if="data['result_selection'] == 'customer_supplier'">Receivable and Payable Accounts</span>
</p>
</div>
<div class="col-3">
<strong>Target Moves:</strong>
<p>
<span t-if="data['target_move'] == 'all'">All Entries</span>
<span t-if="data['target_move'] == 'posted'">All Posted Entries</span>
</p>
</div>
</div>
<table class="table table-sm table-reports">
<thead>
<tr>
<th>Partners</th>
<th class="text-end">
<span>Not due</span>
</th>
<th class="text-end"><span t-esc="data['4']['name']"/></th>
<th class="text-end"><span t-esc="data['3']['name']"/></th>
<th class="text-end"><span t-esc="data['2']['name']"/></th>
<th class="text-end"><span t-esc="data['1']['name']"/></th>
<th class="text-end"><span t-esc="data['0']['name']"/></th>
<th class="text-end">Total</th>
</tr>
<tr t-if="get_partner_lines">
<th>Account Total</th>
<th class="text-end"><span t-esc="get_direction[6]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-end"><span t-esc="get_direction[4]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-end"><span t-esc="get_direction[3]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-end"><span t-esc="get_direction[2]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-end"><span t-esc="get_direction[1]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-end"><span t-esc="get_direction[0]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
<th class="text-end"><span t-esc="get_direction[5]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></th>
</tr>
</thead>
<tbody>
<tr t-foreach="get_partner_lines" t-as="partner">
<td>
<span t-esc="partner['name']"/>
</td>
<td class="text-end">
<span t-esc="partner['direction']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="partner['4']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="partner['3']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="partner['2']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="partner['1']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="partner['0']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="partner['total']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,163 @@
import time
from odoo import api, models, _
from odoo.exceptions import UserError
class ReportFinancial(models.AbstractModel):
_name = 'report.accounting_pdf_reports.report_financial'
_description = 'Financial Reports'
def _compute_account_balance(self, accounts):
""" compute the balance, debit and credit for the provided accounts
"""
mapping = {
'balance': "COALESCE(SUM(debit),0) - COALESCE(SUM(credit), 0) as balance",
'debit': "COALESCE(SUM(debit), 0) as debit",
'credit': "COALESCE(SUM(credit), 0) as credit",
}
res = {}
for account in accounts:
res[account.id] = dict.fromkeys(mapping, 0.0)
if accounts:
tables, where_clause, where_params = self.env['account.move.line']._query_get()
tables = tables.replace('"', '') if tables else "account_move_line"
wheres = [""]
if where_clause.strip():
wheres.append(where_clause.strip())
filters = " AND ".join(wheres)
request = "SELECT account_id as id, " + ', '.join(mapping.values()) + \
" FROM " + tables + \
" WHERE account_id IN %s " \
+ filters + \
" GROUP BY account_id"
params = (tuple(accounts._ids),) + tuple(where_params)
self.env.cr.execute(request, params)
for row in self.env.cr.dictfetchall():
res[row['id']] = row
return res
def _compute_report_balance(self, reports):
'''returns a dictionary with key=the ID of a record and value=the credit, debit and balance amount
computed for this record. If the record is of type :
'accounts' : it's the sum of the linked accounts
'account_type' : it's the sum of leaf accoutns with such an account_type
'account_report' : it's the amount of the related report
'sum' : it's the sum of the children of this record (aka a 'view' record)'''
res = {}
fields = ['credit', 'debit', 'balance']
for report in reports:
if report.id in res:
continue
res[report.id] = dict((fn, 0.0) for fn in fields)
if report.type == 'accounts':
# it's the sum of the linked accounts
res[report.id]['account'] = self._compute_account_balance(report.account_ids)
for value in res[report.id]['account'].values():
for field in fields:
res[report.id][field] += value.get(field)
elif report.type == 'account_type':
# it's the sum the leaf accounts with such an account type
accounts = self.env['account.account'].search(
[('account_type', 'in', report.account_type_ids.mapped('type'))])
res[report.id]['account'] = self._compute_account_balance(accounts)
for value in res[report.id]['account'].values():
for field in fields:
res[report.id][field] += value.get(field)
elif report.type == 'account_report' and report.account_report_id:
# it's the amount of the linked report
res2 = self._compute_report_balance(report.account_report_id)
for key, value in res2.items():
for field in fields:
res[report.id][field] += value[field]
elif report.type == 'sum':
# it's the sum of the children of this account.report
res2 = self._compute_report_balance(report.children_ids)
for key, value in res2.items():
for field in fields:
res[report.id][field] += value[field]
return res
def get_account_lines(self, data):
lines = []
account_report = self.env['account.financial.report'].search(
[('id', '=', data['account_report_id'][0])])
child_reports = account_report._get_children_by_order()
res = self.with_context(data.get('used_context'))._compute_report_balance(child_reports)
if data['enable_filter']:
comparison_res = self.with_context(
data.get('comparison_context'))._compute_report_balance(
child_reports)
for report_id, value in comparison_res.items():
res[report_id]['comp_bal'] = value['balance']
report_acc = res[report_id].get('account')
if report_acc:
for account_id, val in comparison_res[report_id].get('account').items():
report_acc[account_id]['comp_bal'] = val['balance']
for report in child_reports:
vals = {
'name': report.name,
'balance': res[report.id]['balance'] * float(report.sign),
'type': 'report',
'level': bool(report.style_overwrite) and report.style_overwrite or report.level,
'account_type': report.type or False, #used to underline the financial report balances
}
if data['debit_credit']:
vals['debit'] = res[report.id]['debit']
vals['credit'] = res[report.id]['credit']
if data['enable_filter']:
vals['balance_cmp'] = res[report.id]['comp_bal'] * float(report.sign)
lines.append(vals)
if report.display_detail == 'no_detail':
#the rest of the loop is used to display the details of the financial report, so it's not needed here.
continue
if res[report.id].get('account'):
sub_lines = []
for account_id, value in res[report.id]['account'].items():
#if there are accounts to display, we add them to the lines with a level equals to their level in
#the COA + 1 (to avoid having them with a too low level that would conflicts with the level of data
#financial reports for Assets, liabilities...)
flag = False
account = self.env['account.account'].browse(account_id)
vals = {
'name': account.code + ' ' + account.name,
'balance': value['balance'] * float(report.sign) or 0.0,
'type': 'account',
'level': report.display_detail == 'detail_with_hierarchy' and 4,
'account_type': account.account_type,
}
if data['debit_credit']:
vals['debit'] = value['debit']
vals['credit'] = value['credit']
if not self.env.company.currency_id.is_zero(vals['debit']) or not self.env.company.currency_id.is_zero(vals['credit']):
flag = True
if not self.env.company.currency_id.is_zero(vals['balance']):
flag = True
if data['enable_filter']:
vals['balance_cmp'] = value['comp_bal'] * float(report.sign)
if not self.env.company.currency_id.is_zero(vals['balance_cmp']):
flag = True
if flag:
sub_lines.append(vals)
lines += sorted(sub_lines, key=lambda sub_line: sub_line['name'])
return lines
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model') or not self.env.context.get('active_id'):
raise UserError(_("Form content is missing, this report cannot be printed."))
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_id'))
report_lines = self.get_account_lines(data.get('form'))
return {
'doc_ids': self.ids,
'doc_model': model,
'data': data['form'],
'docs': docs,
'time': time,
'get_account_lines': report_lines,
}

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_financial">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="web.internal_layout">
<div class="page">
<h2 t-esc="data['account_report_id'][1]"/>
<div class="row mt32 mb32">
<div class="col-4">
<strong>Target Moves:</strong>
<p>
<span t-if="data['target_move'] == 'all'">All Entries</span>
<span t-if="data['target_move'] == 'posted'">All Posted Entries</span>
</p>
</div>
<div class="col-4">
<p>
<t t-if="data['date_from']"><strong>Date from :</strong> <span t-esc="data['date_from']"/><br/></t>
<t t-if="data['date_to']"><strong>Date to :</strong> <span t-esc="data['date_to']"/></t>
</p>
</div>
</div>
<table class="table table-sm table-reports" t-if="data['debit_credit'] == 1">
<thead>
<tr>
<th>Name</th>
<th class="text-end">Debit</th>
<th class="text-end">Credit</th>
<th class="text-end">Balance</th>
</tr>
</thead>
<tbody>
<tr t-foreach="get_account_lines" t-as="a">
<t t-if="a['level'] != 0">
<t t-if="int(a.get('level')) &gt; 3"><t t-set="style" t-value="'font-weight: normal;'"/></t>
<t t-if="not int(a.get('level')) &gt; 3"><t t-set="style" t-value="'font-weight: bold;'"/></t>
<td>
<span style="color: white;" t-esc="'..' * int(a.get('level', 0))"/>
<span t-att-style="style" t-esc="a.get('name')"/>
</td>
<td class="text-end" style="white-space: text-nowrap;">
<span t-att-style="style" t-esc="a.get('debit')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end" style="white-space: text-nowrap;">
<span t-att-style="style" t-esc="a.get('credit')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end" style="white-space: text-nowrap;">
<span t-att-style="style" t-esc="a.get('balance')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</t>
</tr>
</tbody>
</table>
<table class="table table-sm table-reports" t-if="not data['enable_filter'] and not data['debit_credit']">
<thead>
<tr>
<th>Name</th>
<th class="text-end">Balance</th>
</tr>
</thead>
<tbody>
<tr t-foreach="get_account_lines" t-as="a">
<t t-if="a['level'] != 0">
<t t-if="int(a.get('level')) &gt; 3"><t t-set="style" t-value="'font-weight: normal;'"/></t>
<t t-if="not int(a.get('level')) &gt; 3"><t t-set="style" t-value="'font-weight: bold;'"/></t>
<td>
<span style="color: white;" t-esc="'..' * int(a.get('level', 0))"/>
<span t-att-style="style" t-esc="a.get('name')"/>
</td>
<td class="text-end"><span t-att-style="style" t-esc="a.get('balance')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
</t>
</tr>
</tbody>
</table>
<table class="table table-sm table-reports" t-if="data['enable_filter'] == 1 and not data['debit_credit']">
<thead>
<tr>
<th>Name</th>
<th class="text-end">Balance</th>
<th class="text-end"><span t-esc="data['label_filter']"/></th>
</tr>
</thead>
<tbody>
<tr t-foreach="get_account_lines" t-as="a">
<t t-if="a['level'] != 0">
<t t-if="int(a.get('level')) &gt; 3"><t t-set="style" t-value="'font-weight: normal;'"/></t>
<t t-if="not int(a.get('level')) &gt; 3"><t t-set="style" t-value="'font-weight: bold;'"/></t>
<td>
<span style="color: white;" t-esc="'..'"/>
<span t-att-style="style" t-esc="a.get('name')"/>
</td>
<td class="text-end">
<span t-att-style="style" t-esc="a.get('balance')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-att-style="style" t-esc="a.get('balance_cmp')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</t>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,184 @@
import time
from odoo import api, models, _
from odoo.exceptions import UserError
class ReportGeneralLedger(models.AbstractModel):
_name = 'report.accounting_pdf_reports.report_general_ledger'
_description = 'General Ledger Report'
def _get_account_move_entry(self, accounts, analytic_account_ids,
partner_ids, init_balance,
sortby, display_account):
"""
:param:
accounts: the recordset of accounts
analytic_account_ids: the recordset of analytic accounts
init_balance: boolean value of initial_balance
sortby: sorting by date or partner and journal
display_account: type of account(receivable, payable and both)
Returns a dictionary of accounts with following key and value {
'code': account code,
'name': account name,
'debit': sum of total debit amount,
'credit': sum of total credit amount,
'balance': total balance,
'amount_currency': sum of amount_currency,
'move_lines': list of move line
}
"""
cr = self.env.cr
MoveLine = self.env['account.move.line']
move_lines = {x: [] for x in accounts.ids}
# Prepare initial sql query and Get the initial move lines
if init_balance:
context = dict(self.env.context)
context['date_from'] = self.env.context.get('date_from')
context['date_to'] = False
context['initial_bal'] = True
if analytic_account_ids:
context['analytic_account_ids'] = analytic_account_ids
if partner_ids:
context['partner_ids'] = partner_ids
init_tables, init_where_clause, init_where_params = MoveLine.with_context(context)._query_get()
init_wheres = [""]
if init_where_clause.strip():
init_wheres.append(init_where_clause.strip())
init_filters = " AND ".join(init_wheres)
filters = init_filters.replace('account_move_line__move_id', 'm').replace('account_move_line', 'l')
sql = ("""SELECT 0 AS lid, l.account_id AS account_id, '' AS ldate,
'' AS lcode, 0.0 AS amount_currency,
'' AS analytic_account_id, '' AS lref,
'Initial Balance' AS lname, COALESCE(SUM(l.debit),0.0) AS debit,
COALESCE(SUM(l.credit),0.0) AS credit,
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) as balance,
'' AS lpartner_id,\
'' AS move_name, '' AS move_id, '' AS currency_code,\
NULL AS currency_id,\
'' AS invoice_id, '' AS invoice_type, '' AS invoice_number,\
'' AS partner_name\
FROM account_move_line l\
LEFT JOIN account_move m ON (l.move_id=m.id)\
LEFT JOIN res_currency c ON (l.currency_id=c.id)\
LEFT JOIN res_partner p ON (l.partner_id=p.id)\
JOIN account_journal j ON (l.journal_id=j.id)\
WHERE l.account_id IN %s""" + filters + ' GROUP BY l.account_id')
params = (tuple(accounts.ids),) + tuple(init_where_params)
cr.execute(sql, params)
for row in cr.dictfetchall():
move_lines[row.pop('account_id')].append(row)
sql_sort = 'l.date, l.move_id'
if sortby == 'sort_journal_partner':
sql_sort = 'j.code, p.name, l.move_id'
# Prepare sql query base on selected parameters from wizard
context = dict(self.env.context)
if analytic_account_ids:
context['analytic_account_ids'] = analytic_account_ids
if partner_ids:
context['partner_ids'] = partner_ids
tables, where_clause, where_params = MoveLine.with_context(context)._query_get()
wheres = [""]
if where_clause.strip():
wheres.append(where_clause.strip())
filters = " AND ".join(wheres)
filters = filters.replace('account_move_line__move_id', 'm').replace('account_move_line', 'l')
# Get move lines base on sql query and Calculate the total balance of move lines
sql = ('''SELECT l.id AS lid, l.account_id AS account_id,
l.date AS ldate, j.code AS lcode, l.currency_id,
l.amount_currency, '' AS analytic_account_id,
l.ref AS lref, l.name AS lname, COALESCE(l.debit,0) AS debit,
COALESCE(l.credit,0) AS credit,
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit), 0) AS balance,\
m.name AS move_name, c.symbol AS currency_code,
p.name AS partner_name\
FROM account_move_line l\
JOIN account_move m ON (l.move_id=m.id)\
LEFT JOIN res_currency c ON (l.currency_id=c.id)\
LEFT JOIN res_partner p ON (l.partner_id=p.id)\
JOIN account_journal j ON (l.journal_id=j.id)\
JOIN account_account acc ON (l.account_id = acc.id) \
WHERE l.account_id IN %s ''' + filters + ''' GROUP BY l.id,
l.account_id, l.date, j.code, l.currency_id, l.amount_currency,
l.ref, l.name, m.name, c.symbol, p.name ORDER BY ''' + sql_sort)
params = (tuple(accounts.ids),) + tuple(where_params)
cr.execute(sql, params)
for row in cr.dictfetchall():
balance = 0
for line in move_lines.get(row['account_id']):
balance += line['debit'] - line['credit']
row['balance'] += balance
move_lines[row.pop('account_id')].append(row)
# Calculate the debit, credit and balance for Accounts
account_res = []
for account in accounts:
currency = account.currency_id and account.currency_id or self.env.company.currency_id
res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance'])
res['code'] = account.code
res['name'] = account.name
res['move_lines'] = move_lines[account.id]
for line in res.get('move_lines'):
res['debit'] += line['debit']
res['credit'] += line['credit']
res['balance'] = line['balance']
if display_account == 'all':
account_res.append(res)
if display_account == 'movement' and res.get('move_lines'):
account_res.append(res)
if display_account == 'not_zero' and not currency.is_zero(res['balance']):
account_res.append(res)
return account_res
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model'):
raise UserError(_("Form content is missing, this report cannot be printed."))
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_ids', []))
init_balance = data['form'].get('initial_balance', True)
sortby = data['form'].get('sortby', 'sort_date')
display_account = data['form']['display_account']
codes = []
if data['form'].get('journal_ids', False):
codes = [journal.code for journal in
self.env['account.journal'].search(
[('id', 'in', data['form']['journal_ids'])])]
analytic_account_ids = False
if data['form'].get('analytic_account_ids', False):
analytic_account_ids = self.env['account.analytic.account'].search(
[('id', 'in', data['form']['analytic_account_ids'])])
partner_ids = False
if data['form'].get('partner_ids', False):
partner_ids = self.env['res.partner'].search(
[('id', 'in', data['form']['partner_ids'])])
if model == 'account.account':
accounts = docs
else:
domain = []
if data['form'].get('account_ids', False):
domain.append(('id', 'in', data['form']['account_ids']))
accounts = self.env['account.account'].search(domain)
accounts_res = self.with_context(
data['form'].get('used_context', {}))._get_account_move_entry(
accounts,
analytic_account_ids,
partner_ids,
init_balance, sortby, display_account)
return {
'doc_ids': docids,
'doc_model': model,
'data': data['form'],
'docs': docs,
'time': time,
'Accounts': accounts_res,
'print_journal': codes,
'accounts': accounts,
'partner_ids': partner_ids,
'analytic_account_ids': analytic_account_ids,
}

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_general_ledger">
<t t-call="web.html_container">
<t t-set="data_report_margin_top" t-value="12"/>
<t t-set="data_report_header_spacing" t-value="9"/>
<t t-set="data_report_dpi" t-value="110"/>
<t t-call="web.internal_layout">
<div class="page">
<h2><span t-esc="res_company.name"/>: General ledger</h2>
<div class="row mt32">
<div class="col-4">
<strong>Journals:</strong>
<p t-esc="', '.join([ lt or '' for lt in print_journal ])"/>
</div>
<t groups="analytic.group_analytic_accounting">
<t t-if="analytic_account_ids">
<div class="col-4">
<strong>Analytic Accounts:</strong>
<p t-esc="', '.join([aa.name or '' for aa in analytic_account_ids ])"/>
</div>
</t>
</t>
<div class="col-4">
<strong>Display Account</strong>
<p>
<span t-if="data['display_account'] == 'all'">All accounts'</span>
<span t-if="data['display_account'] == 'movement'">With movements</span>
<span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span>
</p>
</div>
<div class="col-4">
<strong>Target Moves:</strong>
<p t-if="data['target_move'] == 'all'">All Entries</p>
<p t-if="data['target_move'] == 'posted'">All Posted Entries</p>
</div>
</div>
<div class="row mb32">
<div class="col-4">
<strong>Sorted By:</strong>
<p t-if="data['sortby'] == 'sort_date'">Date</p>
<p t-if="data['sortby'] == 'sort_journal_partner'">Journal and Partner</p>
</div>
<div class="col-4">
<t t-if="data['date_from']"><strong>Date from :</strong> <span t-esc="data['date_from']"/><br/></t>
<t t-if="data['date_to']"><strong>Date to :</strong> <span t-esc="data['date_to']"/></t>
</div>
</div>
<table class="table table-sm table-reports">
<thead>
<tr class="text-center">
<th>Date</th>
<th>JRNL</th>
<th>Partner</th>
<th>Ref</th>
<th>Move</th>
<t groups="analytic.group_analytic_accounting">
<th>Analytic Account</th>
</t>
<th>Entry Label</th>
<th>Debit</th>
<th>Credit</th>
<th>Balance</th>
<th groups="base.group_multi_currency">Currency</th>
</tr>
</thead>
<tbody>
<t t-foreach="Accounts" t-as="account">
<tr style="font-weight: bold;">
<td colspan="6">
<span style="color: white;" t-esc="'..'"/>
<span t-esc="account['code']"/>
<span t-esc="account['name']"/>
</td>
<t groups="analytic.group_analytic_accounting">
<td></td>
</t>
<td class="text-end">
<span t-esc="account['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="account['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="account['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td groups="base.group_multi_currency"/>
</tr>
<tr t-foreach="account['move_lines']" t-as="line">
<td><span t-esc="line['ldate']"/></td>
<td><span t-esc="line['lcode']"/></td>
<td><span t-esc="line['partner_name']"/></td>
<td><span t-if="line['lref']" t-esc="line['lref']"/></td>
<td><span t-esc="line['move_name']"/></td>
<t groups="analytic.group_analytic_accounting">
<td><span t-esc="line['analytic_account_id']"/></td>
</t>
<td><span t-esc="line['lname']"/></td>
<td class="text-end">
<span t-esc="line['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="line['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="line['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end" groups="base.group_multi_currency">
<span t-esc="line['amount_currency'] if line['amount_currency'] and line['amount_currency'] > 0.00 else ''"/>
<span t-esc="line['currency_code'] if line['amount_currency'] and line['amount_currency'] > 0.00 else ''"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,117 @@
import time
from odoo import api, models, _
from odoo.exceptions import UserError
class ReportJournal(models.AbstractModel):
_name = 'report.accounting_pdf_reports.report_journal'
_description = 'Journal Audit Report'
def lines(self, target_move, journal_ids, sort_selection, data):
if isinstance(journal_ids, int):
journal_ids = [journal_ids]
move_state = ['draft', 'posted']
if target_move == 'posted':
move_state = ['posted']
query_get_clause = self._get_query_get_clause(data)
params = [tuple(move_state), tuple(journal_ids)] + query_get_clause[2]
query = 'SELECT "account_move_line".id FROM ' + query_get_clause[0] + ', account_move am, account_account acc WHERE "account_move_line".account_id = acc.id AND "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' ORDER BY '
if sort_selection == 'date':
query += '"account_move_line".date'
else:
query += 'am.name'
query += ', "account_move_line".move_id'
self.env.cr.execute(query, tuple(params))
ids = (x[0] for x in self.env.cr.fetchall())
return self.env['account.move.line'].browse(ids)
def _sum_debit(self, data, journal_id):
move_state = ['draft', 'posted']
if data['form'].get('target_move', 'all') == 'posted':
move_state = ['posted']
query_get_clause = self._get_query_get_clause(data)
params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[2]
self.env.cr.execute('SELECT SUM(debit) FROM ' + query_get_clause[0] + ', account_move am '
'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' ',
tuple(params))
return self.env.cr.fetchone()[0] or 0.0
def _sum_credit(self, data, journal_id):
move_state = ['draft', 'posted']
if data['form'].get('target_move', 'all') == 'posted':
move_state = ['posted']
query_get_clause = self._get_query_get_clause(data)
params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[2]
self.env.cr.execute('SELECT SUM(credit) FROM ' + query_get_clause[0] + ', account_move am '
'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' ',
tuple(params))
return self.env.cr.fetchone()[0] or 0.0
def _get_taxes(self, data, journal_id):
move_state = ['draft', 'posted']
if data['form'].get('target_move', 'all') == 'posted':
move_state = ['posted']
query_get_clause = self._get_query_get_clause(data)
params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[2]
query = """
SELECT rel.account_tax_id, SUM("account_move_line".balance) AS base_amount
FROM account_move_line_account_tax_rel rel, """ + query_get_clause[0] + """
LEFT JOIN account_move am ON "account_move_line".move_id = am.id
WHERE "account_move_line".id = rel.account_move_line_id
AND am.state IN %s
AND "account_move_line".journal_id IN %s
AND """ + query_get_clause[1] + """
GROUP BY rel.account_tax_id"""
self.env.cr.execute(query, tuple(params))
ids = []
base_amounts = {}
for row in self.env.cr.fetchall():
ids.append(row[0])
base_amounts[row[0]] = row[1]
res = {}
for tax in self.env['account.tax'].browse(ids):
self.env.cr.execute('SELECT sum(debit - credit) FROM ' + query_get_clause[0] + ', account_move am '
'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' AND tax_line_id = %s',
tuple(params + [tax.id]))
res[tax] = {
'base_amount': base_amounts[tax.id],
'tax_amount': self.env.cr.fetchone()[0] or 0.0,
}
if journal_id.type == 'sale':
#sales operation are credits
res[tax]['base_amount'] = res[tax]['base_amount'] * -1
res[tax]['tax_amount'] = res[tax]['tax_amount'] * -1
return res
def _get_query_get_clause(self, data):
return self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get()
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form'):
raise UserError(_("Form content is missing, this report cannot be printed."))
target_move = data['form'].get('target_move', 'all')
sort_selection = data['form'].get('sort_selection', 'date')
res = {}
for journal in data['form']['journal_ids']:
res[journal] = self.with_context(data['form'].get('used_context', {})).lines(target_move, journal, sort_selection, data)
return {
'doc_ids': data['form']['journal_ids'],
'doc_model': self.env['account.journal'],
'data': data,
'docs': self.env['account.journal'].browse(data['form']['journal_ids']),
'time': time,
'lines': res,
'sum_credit': self._sum_credit,
'sum_debit': self._sum_debit,
'get_taxes': self._get_taxes,
}

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_journal">
<t t-call="web.html_container">
<t t-set="data_report_margin_top" t-value="12"/>
<t t-set="data_report_header_spacing" t-value="9"/>
<t t-set="data_report_dpi" t-value="110"/>
<t t-foreach="docs" t-as="o">
<t t-call="web.internal_layout">
<div class="page">
<h2><t t-esc="o.name"/> Journal</h2>
<div class="row mt32">
<div class="col-3">
<strong>Company:</strong>
<p t-esc="res_company.name"/>
</div>
<div class="col-3">
<strong>Journal:</strong>
<p t-esc="o.name"/>
</div>
<div class="col-3">
<strong>Entries Sorted By:</strong>
<p t-if="data['form'].get('sort_selection') != 'l.date'">Journal Entry Number</p>
<p t-if="data['form'].get('sort_selection') == 'l.date'">Date</p>
</div>
<div class="col-3">
<strong>Target Moves:</strong>
<p t-if="data['form']['target_move'] == 'all'">All Entries</p>
<p t-if="data['form']['target_move'] == 'posted'">All Posted Entries</p>
</div>
</div>
<table class="table table-sm table-reports">
<thead>
<tr>
<th>Move</th>
<th>Date</th>
<th>Account</th>
<th>Partner</th>
<th>Label</th>
<th>Debit</th>
<th>Credit</th>
<th t-if="data['form']['amount_currency']">Currency</th>
</tr>
</thead>
<tbody>
<tr t-foreach="lines[o.id]" t-as="aml">
<td><span t-esc="aml.move_id.name != '/' and aml.move_id.name or ('*'+str(aml.move_id.id))"/></td>
<td><span t-field="aml.date"/></td>
<td><span t-field="aml.account_id.code"/></td>
<td><span t-esc="aml.sudo().partner_id and aml.sudo().partner_id.name and aml.sudo().partner_id.name[:23] or ''"/></td>
<td><span t-esc="aml.name and aml.name[:35]"/></td>
<td><span t-esc="aml.debit" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
<td><span t-esc="aml.credit" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
<td t-if="data['form']['amount_currency'] and aml.amount_currency">
<span t-esc="aml.amount_currency" t-options="{'widget': 'monetary', 'display_currency': aml.currency_id}"/>
</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-4 pull-right">
<table>
<tr>
<td><strong>Total</strong></td>
<td><span t-esc="sum_debit(data, o)" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
<td><span t-esc="sum_credit(data, o)" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
</tr>
</table>
</div>
</div>
<div class="row">
<div class="col-4">
<table class="table table-sm table-reports">
<thead>
<tr><th colspan="3">Tax Declaration</th></tr>
<tr>
<th>Name</th>
<th>Base Amount</th>
<th>Tax Amount</th>
</tr>
</thead>
<tbody>
<t t-set="taxes" t-value="get_taxes(data, o)"/>
<tr t-foreach="taxes" t-as="tax">
<td><span t-esc="tax.name"/></td>
<td><span t-esc="taxes[tax]['base_amount']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
<td><span t-esc="taxes[tax]['tax_amount']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_journal_entries">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-foreach="docs" t-as="o">
<div class="page" style="font-size:15px;">
<div>
<h3>
<span t-field="o.name"/>
</h3>
</div>
<br></br>
<div class="row">
<table width="100%" class="table-bordered">
<tr>
<td>Journal:
<span t-field="o.journal_id.name"/>
</td>
<td>
Date:
<span t-field="o.date" t-options="{'widget': 'date'}"/>
</td>
</tr>
<tr>
<td>
Partner:
<span t-field="o.partner_id.display_name"/>
</td>
<td>
Reference:
<span t-field="o.ref"/>
</td>
</tr>
</table>
</div>
<div class="row">
<br></br>
<table width="100%" class="table-bordered">
<thead>
<tr>
<th>Account</th>
<th>Date</th>
<th>Partner</th>
<th>Label</th>
<th>Analytic Account</th>
<th>Debit</th>
<th>Credit</th>
</tr>
</thead>
<tbody>
<t t-set="total_credit" t-value="0"/>
<t t-set="total_debit" t-value="0"/>
<t t-foreach="o.line_ids" t-as="line">
<tr>
<td>
<span t-field="line.account_id.name"/>
</td>
<td>
<span t-field="line.date" t-options="{'widget': 'date'}"/>
</td>
<td>
<span t-field="line.partner_id.display_name"/>
</td>
<td>
<span t-field="line.name"/>
</td>
<td>
<span t-field="line.analytic_account_id.display_name"/>
</td>
<td class="text-end">
<span t-field="line.debit"
t-options="{'widget': 'monetary', 'display_currency': line.currency_id}"/>
</td>
<td class="text-end">
<span t-field="line.credit"
t-options="{'widget': 'monetary', 'display_currency': line.currency_id}"/>
</td>
<t t-set="total_credit" t-value="total_credit + line.credit"/>
<t t-set="total_debit" t-value="total_debit + line.debit"/>
</tr>
</t>
</tbody>
<tfooter>
<tr>
<td colspan="5"></td>
<td class="text-end">
<span t-esc="total_debit"
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="total_credit"
t-options="{'widget': 'monetary', 'display_currency': o.currency_id}"/>
</td>
</tr>
</tfooter>
</table>
</div>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,122 @@
import time
from odoo import api, models, _
from odoo.exceptions import UserError
class ReportPartnerLedger(models.AbstractModel):
_name = 'report.accounting_pdf_reports.report_partnerledger'
_description = 'Partner Ledger Report'
def _lines(self, data, partner):
full_account = []
currency = self.env['res.currency']
query_get_data = self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get()
reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
params = [partner.id, tuple(data['computed']['move_state']), tuple(data['computed']['account_ids'])] + query_get_data[2]
query = """
SELECT "account_move_line".id, "account_move_line".date, j.code, acc.name->>'en_US' as a_name, "account_move_line".ref, m.name as move_name, "account_move_line".name, "account_move_line".debit, "account_move_line".credit, "account_move_line".amount_currency,"account_move_line".currency_id, c.symbol AS currency_code
FROM """ + query_get_data[0] + """
LEFT JOIN account_journal j ON ("account_move_line".journal_id = j.id)
LEFT JOIN account_account acc ON ("account_move_line".account_id = acc.id)
LEFT JOIN res_currency c ON ("account_move_line".currency_id=c.id)
LEFT JOIN account_move m ON (m.id="account_move_line".move_id)
WHERE "account_move_line".partner_id = %s
AND m.state IN %s
AND "account_move_line".account_id IN %s AND """ + query_get_data[1] + reconcile_clause + """
ORDER BY "account_move_line".date"""
self.env.cr.execute(query, tuple(params))
res = self.env.cr.dictfetchall()
sum = 0.0
lang_code = self.env.context.get('lang') or 'en_US'
lang = self.env['res.lang']
lang_id = lang._lang_get(lang_code)
date_format = lang_id.date_format
for r in res:
r['date'] = r['date']
r['displayed_name'] = '-'.join(
r[field_name] for field_name in ('move_name', 'ref', 'name')
if r[field_name] not in (None, '', '/')
)
sum += r['debit'] - r['credit']
r['progress'] = sum
r['currency_id'] = currency.browse(r.get('currency_id'))
full_account.append(r)
return full_account
def _sum_partner(self, data, partner, field):
if field not in ['debit', 'credit', 'debit - credit']:
return
result = 0.0
query_get_data = self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get()
reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
params = [partner.id, tuple(data['computed']['move_state']), tuple(data['computed']['account_ids'])] + query_get_data[2]
query = """SELECT sum(""" + field + """)
FROM """ + query_get_data[0] + """, account_move AS m
WHERE "account_move_line".partner_id = %s
AND m.id = "account_move_line".move_id
AND m.state IN %s
AND account_id IN %s
AND """ + query_get_data[1] + reconcile_clause
self.env.cr.execute(query, tuple(params))
contemp = self.env.cr.fetchone()
if contemp is not None:
result = contemp[0] or 0.0
return result
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form'):
raise UserError(_("Form content is missing, this report cannot be printed."))
data['computed'] = {}
obj_partner = self.env['res.partner']
query_get_data = self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get()
data['computed']['move_state'] = ['draft', 'posted']
if data['form'].get('target_move', 'all') == 'posted':
data['computed']['move_state'] = ['posted']
result_selection = data['form'].get('result_selection', 'customer')
if result_selection == 'supplier':
data['computed']['ACCOUNT_TYPE'] = ['liability_payable']
elif result_selection == 'customer':
data['computed']['ACCOUNT_TYPE'] = ['asset_receivable']
else:
data['computed']['ACCOUNT_TYPE'] = ['asset_receivable', 'liability_payable']
self.env.cr.execute("""
SELECT a.id
FROM account_account a
WHERE a.account_type IN %s
AND a.active""", (tuple(data['computed']['ACCOUNT_TYPE']),))
data['computed']['account_ids'] = [a for (a,) in self.env.cr.fetchall()]
params = [tuple(data['computed']['move_state']), tuple(data['computed']['account_ids'])] + query_get_data[2]
reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
query = """
SELECT DISTINCT "account_move_line".partner_id
FROM """ + query_get_data[0] + """, account_account AS account, account_move AS am
WHERE "account_move_line".partner_id IS NOT NULL
AND "account_move_line".account_id = account.id
AND am.id = "account_move_line".move_id
AND am.state IN %s
AND "account_move_line".account_id IN %s
AND account.active
AND """ + query_get_data[1] + reconcile_clause
self.env.cr.execute(query, tuple(params))
if data['form']['partner_ids']:
partner_ids = data['form']['partner_ids']
else:
partner_ids = [res['partner_id'] for res in
self.env.cr.dictfetchall()]
partners = obj_partner.browse(partner_ids)
partners = sorted(partners, key=lambda x: (x.ref or '', x.name or ''))
return {
'doc_ids': partner_ids,
'doc_model': self.env['res.partner'],
'data': data,
'docs': partners,
'time': time,
'lines': self._lines,
'sum_partner': self._sum_partner,
}

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_partnerledger">
<t t-call="web.html_container">
<t t-call="web.internal_layout">
<t t-set="data_report_margin_top" t-value="12"/>
<t t-set="data_report_header_spacing" t-value="9"/>
<t t-set="data_report_dpi" t-value="110"/>
<div class="page">
<h2>Partner Ledger</h2>
<div class="row">
<div class="col-3">
<strong>Company:</strong>
<p t-esc="res_company.name"/>
</div>
<div class="col-3">
<t t-if="data['form']['date_from']">
<strong>Date from :</strong>
<span t-esc="data['form']['date_from']"/>
<br/>
</t>
<t t-if="data['form']['date_to']">
<strong>Date to :</strong>
<span t-esc="data['form']['date_to']"/>
</t>
</div>
<div class="col-3">
<strong>Target Moves:</strong>
<p t-if="data['form']['target_move'] == 'all'">All Entries</p>
<p t-if="data['form']['target_move'] == 'posted'">All Posted Entries</p>
</div>
</div>
<table class="table table-sm table-reports">
<thead>
<tr>
<th>Date</th>
<th>JRNL</th>
<th>Account</th>
<th>Ref</th>
<th>Debit</th>
<th>Credit</th>
<th>Balance</th>
<th t-if="data['form']['amount_currency']">Currency</th>
</tr>
</thead>
<t t-foreach="docs" t-as="o">
<tbody>
<tr>
<td colspan="4">
<strong t-esc="o.ref"/>
-
<strong t-esc="o.name"/>
</td>
<td class="text-end">
<strong t-esc="sum_partner(data, o, 'debit')"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<strong t-esc="sum_partner(data, o, 'credit')"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<strong t-esc="sum_partner(data, o, 'debit - credit')"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</tr>
<tr t-foreach="lines(data, o)" t-as="line">
<td>
<span t-esc="line['date']"/>
</td>
<td>
<span t-esc="line['code']"/>
</td>
<td>
<span t-esc="line['a_name']"/>
</td>
<td>
<span t-esc="line['displayed_name']"/>
</td>
<td class="text-end">
<span t-esc="line['debit']"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="line['credit']"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-esc="line['progress']"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end" t-if="data['form']['amount_currency']">
<t t-if="line['currency_id']">
<span t-esc="line['amount_currency']"
t-options="{'widget': 'monetary', 'display_currency': line['currency_id']}"/>
</t>
</td>
</tr>
</tbody>
</t>
</table>
</div>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,70 @@
from odoo import api, models, _
from odoo.exceptions import UserError
class ReportTax(models.AbstractModel):
_name = 'report.accounting_pdf_reports.report_tax'
_description = 'Tax Report'
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form'):
raise UserError(_("Form content is missing, this report cannot be printed."))
return {
'data': data['form'],
'lines': self.get_lines(data.get('form')),
}
def _sql_from_amls_one(self):
sql = """SELECT "account_move_line".tax_line_id, COALESCE(SUM("account_move_line".debit-"account_move_line".credit), 0)
FROM %s
WHERE %s GROUP BY "account_move_line".tax_line_id"""
return sql
def _sql_from_amls_two(self):
sql = """SELECT r.account_tax_id, COALESCE(SUM("account_move_line".debit-"account_move_line".credit), 0)
FROM %s
INNER JOIN account_move_line_account_tax_rel r ON ("account_move_line".id = r.account_move_line_id)
INNER JOIN account_tax t ON (r.account_tax_id = t.id)
WHERE %s GROUP BY r.account_tax_id"""
return sql
def _compute_from_amls(self, options, taxes):
#compute the tax amount
sql = self._sql_from_amls_one()
tables, where_clause, where_params = self.env['account.move.line']._query_get()
query = sql % (tables, where_clause)
self.env.cr.execute(query, where_params)
results = self.env.cr.fetchall()
for result in results:
if result[0] in taxes:
taxes[result[0]]['tax'] = abs(result[1])
#compute the net amount
sql2 = self._sql_from_amls_two()
query = sql2 % (tables, where_clause)
self.env.cr.execute(query, where_params)
results = self.env.cr.fetchall()
for result in results:
if result[0] in taxes:
taxes[result[0]]['net'] = abs(result[1])
@api.model
def get_lines(self, options):
taxes = {}
for tax in self.env['account.tax'].search([('type_tax_use', '!=', 'none')]):
if tax.children_tax_ids:
for child in tax.children_tax_ids:
if child.type_tax_use != 'none':
continue
taxes[child.id] = {'tax': 0, 'net': 0, 'name': child.name, 'type': tax.type_tax_use}
else:
taxes[tax.id] = {'tax': 0, 'net': 0, 'name': tax.name, 'type': tax.type_tax_use}
self.with_context(date_from=options['date_from'], date_to=options['date_to'],
state=options['target_move'],
strict_range=True)._compute_from_amls(options, taxes)
groups = dict((tp, []) for tp in ['sale', 'purchase'])
for tax in taxes.values():
if tax['tax']:
groups[tax['type']].append(tax)
return groups

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_tax">
<t t-call="web.html_container">
<t t-set="data_report_margin_top" t-value="12"/>
<t t-set="data_report_header_spacing" t-value="9"/>
<t t-set="data_report_dpi" t-value="110"/>
<t t-call="web.internal_layout">
<div class="page">
<h3>Tax Report</h3>
<div class="row">
<div class="col-4">
<strong>Company:</strong>
<p t-esc="res_company.name"/>
</div>
<div class="col-4">
<t>
<strong>Date from :</strong>
<span t-esc="data['date_from']"/>
</t>
<br/>
<t>
<strong>Date to :</strong>
<span t-esc="data['date_to']"/>
</t>
</div>
<div class="col-4">
<strong>Target Moves:</strong>
<p>
<span t-if="data['target_move'] == 'all'">All Entries</span>
<span t-if="data['target_move'] == 'posted'">All Posted Entries</span>
</p>
</div>
</div>
<table class="table table-sm table-reports">
<thead>
<tr align="left">
<th>Sale</th>
<th>Net</th>
<th>Tax</th>
</tr>
</thead>
<tr align="left" t-foreach="lines['sale']" t-as="line">
<td>
<span t-esc="line.get('name')"/>
</td>
<td>
<span t-att-style="style" t-esc="line.get('net')"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td>
<span t-att-style="style" t-esc="line.get('tax')"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</tr>
<br/>
<tr align="left">
<td>
<strong>Purchase</strong>
</td>
<td></td>
<td></td>
</tr>
<tr align="left" t-foreach="lines['purchase']" t-as="line">
<td>
<span t-esc="line.get('name')"/>
</td>
<td>
<span t-att-style="style" t-esc="line.get('net')"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td>
<span t-att-style="style" t-esc="line.get('tax')"
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</tr>
</table>
</div>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,90 @@
import time
from odoo import api, models, _
from odoo.exceptions import UserError
class ReportTrialBalance(models.AbstractModel):
_name = 'report.accounting_pdf_reports.report_trialbalance'
_description = 'Trial Balance Report'
def _get_accounts(self, accounts, display_account):
""" compute the balance, debit and credit for the provided accounts
:Arguments:
`accounts`: list of accounts record,
`display_account`: it's used to display either all accounts or those accounts which balance is > 0
:Returns a list of dictionary of Accounts with following key and value
`name`: Account name,
`code`: Account code,
`credit`: total amount of credit,
`debit`: total amount of debit,
`balance`: total amount of balance,
"""
account_result = {}
# Prepare sql query base on selected parameters from wizard
tables, where_clause, where_params = self.env['account.move.line']._query_get()
tables = tables.replace('"','')
if not tables:
tables = 'account_move_line'
wheres = [""]
if where_clause.strip():
wheres.append(where_clause.strip())
filters = " AND ".join(wheres)
# compute the balance, debit and credit for the provided accounts
request = ("SELECT account_id AS id, SUM(debit) AS debit, SUM(credit) AS credit, "
"(SUM(debit) - SUM(credit)) AS balance" +\
" FROM " + tables + " WHERE account_id IN %s " + filters + " GROUP BY account_id")
params = (tuple(accounts.ids),) + tuple(where_params)
self.env.cr.execute(request, params)
for row in self.env.cr.dictfetchall():
account_result[row.pop('id')] = row
account_res = []
for account in accounts:
res = dict((fn, 0.0) for fn in ['credit', 'debit', 'balance'])
currency = account.currency_id and account.currency_id or self.env.company.currency_id
res['code'] = account.code
res['name'] = account.name
if account.id in account_result:
res['debit'] = account_result[account.id].get('debit')
res['credit'] = account_result[account.id].get('credit')
res['balance'] = account_result[account.id].get('balance')
if display_account == 'all':
account_res.append(res)
if display_account == 'not_zero' and not currency.is_zero(res['balance']):
account_res.append(res)
if display_account == 'movement' and (not currency.is_zero(res['debit']) or not currency.is_zero(res['credit'])):
account_res.append(res)
return account_res
@api.model
def _get_report_values(self, docids, data=None):
if not data.get('form') or not self.env.context.get('active_model'):
raise UserError(_("Form content is missing, this report cannot be printed."))
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_ids', []))
display_account = data['form'].get('display_account')
accounts = docs if model == 'account.account' else self.env['account.account'].search([])
context = data['form'].get('used_context')
analytic_accounts = []
if data['form'].get('analytic_account_ids'):
analytic_account_ids = self.env['account.analytic.account'].browse(data['form'].get('analytic_account_ids'))
context['analytic_account_ids'] = analytic_account_ids
analytic_accounts = [account.name for account in analytic_account_ids]
account_res = self.with_context(context)._get_accounts(accounts, display_account)
codes = []
if data['form'].get('journal_ids', False):
codes = [journal.code for journal in
self.env['account.journal'].search(
[('id', 'in', data['form']['journal_ids'])])]
return {
'doc_ids': self.ids,
'doc_model': model,
'data': data['form'],
'docs': docs,
'print_journal': codes,
'analytic_accounts': analytic_accounts,
'time': time,
'Accounts': account_res,
}

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_trialbalance">
<t t-call="web.html_container">
<t t-set="data_report_margin_top" t-value="12"/>
<t t-set="data_report_header_spacing" t-value="9"/>
<t t-set="data_report_dpi" t-value="110"/>
<t t-call="web.internal_layout">
<div class="page">
<h2><span t-esc="res_company.name"/>: Trial Balance</h2>
<div class="row mt32">
<div class="col-4">
<strong>Display Account:</strong>
<p>
<span t-if="data['display_account'] == 'all'">All accounts</span>
<span t-if="data['display_account'] == 'movement'">With movements</span>
<span t-if="data['display_account'] == 'not_zero'">With balance not equal to zero</span>
</p>
</div>
<div class="col-4">
<p>
<t t-if="data['date_from']"><strong>Date from :</strong> <span t-esc="data['date_from']"/><br/></t>
<t t-if="data['date_to']"><strong>Date to :</strong> <span t-esc="data['date_to']"/></t>
</p>
</div>
<div class="col-4">
<strong>Target Moves:</strong>
<p>
<span t-if="data['target_move'] == 'all'">All Entries</span>
<span t-if="data['target_move'] == 'posted'">All Posted Entries</span>
</p>
</div>
</div>
<div class="row mt32">
<div class="col-6">
<strong>Journals:</strong>
<p t-esc="', '.join([ lt or '' for lt in print_journal ])"/>
</div>
<div class="col-6">
<t t-if="analytic_accounts">
<strong>Analytic Accounts:</strong>
<p t-esc="', '.join([ analytic_account or '' for analytic_account in analytic_accounts ])"/>
</t>
</div>
</div>
<table class="table table-sm table-reports">
<thead>
<tr>
<th>Code</th>
<th>Account</th>
<th class="text-end">Debit</th>
<th class="text-end">Credit</th>
<th class="text-end">Balance</th>
</tr>
</thead>
<tbody>
<tr t-foreach="Accounts" t-as="account">
<td>
<span t-att-style="style" t-esc="account['code']"/>
</td>
<td>
<span style="color: white;" t-esc="'..'"/>
<span t-att-style="style" t-esc="account['name']"/>
</td>
<td class="text-end">
<span t-att-style="style" t-esc="account['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-att-style="style" t-esc="account['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
<td class="text-end">
<span t-att-style="style" t-esc="account['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,25 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_financial_report_accountant,access.account.financial.report.manager,model_account_financial_report,account.group_account_user,1,1,1,1
access_account_report_general_ledger,access.account.report.general.ledger,model_account_report_general_ledger,account.group_account_user,1,1,1,1
access_account_balance_report,access.account.balance.report,model_account_balance_report,account.group_account_user,1,1,1,1
access_account_report_partner_ledger,access.account.report.partner.ledger,model_account_report_partner_ledger,account.group_account_invoice,1,1,1,1
access_accounting_report,access.accounting.report,model_accounting_report,account.group_account_user,1,1,1,1
access_account_aged_trial_balance,access.account.aged.trial.balance,model_account_aged_trial_balance,account.group_account_user,1,1,1,1
access_account_tax_report,access.account.tax.report.wizard,model_account_tax_report_wizard,account.group_account_user,1,1,1,1
access_account_financial_report_accountant_bm,access.account.financial.report.bmanager,model_account_financial_report,account.group_account_manager,1,1,1,1
access_account_report_general_ledger_bm,access.account.report.general.ledger.bmanager,model_account_report_general_ledger,account.group_account_manager,1,1,1,1
access_account_balance_report_bm,access.account.balance.report.bmanager,model_account_balance_report,account.group_account_manager,1,1,1,1
access_account_report_partner_ledger_bm,access.account.report.partner.ledger.bmanager,model_account_report_partner_ledger,account.group_account_manager,1,1,1,1
access_accounting_report_bm,access.accounting.report.bmanager,model_accounting_report,account.group_account_manager,1,1,1,1
access_account_aged_trial_balance_bm,access.account.aged.trial.balance.bmanager,model_account_aged_trial_balance,account.group_account_manager,1,1,1,1
access_account_tax_report_bm,access.account.tax.report.wizard.bmanager,model_account_tax_report_wizard,account.group_account_manager,1,1,1,1
access_account_print_journal_bm,access.account.account.print.journal.bmanager,model_account_print_journal,account.group_account_manager,1,1,1,1
access_account_common_journal_report,access.account.common.journal.report,model_account_common_journal_report,account.group_account_user,1,1,1,0
access_account_print_journal,access.account.print.journal,model_account_print_journal,account.group_account_user,1,1,1,0
access_account_common_account_report,access_account_common_account_report,model_account_common_account_report,base.group_user,1,0,0,0
access_account_common_partner_report,access_account_common_partner_report,model_account_common_partner_report,base.group_user,1,0,0,0
access_account_common_report,access_account_common_report,accounting_pdf_reports.model_account_common_report,base.group_user,1,0,0,0
access_account_account_type,access_account_account_type,accounting_pdf_reports.model_account_account_type,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_financial_report_accountant access.account.financial.report.manager model_account_financial_report account.group_account_user 1 1 1 1
3 access_account_report_general_ledger access.account.report.general.ledger model_account_report_general_ledger account.group_account_user 1 1 1 1
4 access_account_balance_report access.account.balance.report model_account_balance_report account.group_account_user 1 1 1 1
5 access_account_report_partner_ledger access.account.report.partner.ledger model_account_report_partner_ledger account.group_account_invoice 1 1 1 1
6 access_accounting_report access.accounting.report model_accounting_report account.group_account_user 1 1 1 1
7 access_account_aged_trial_balance access.account.aged.trial.balance model_account_aged_trial_balance account.group_account_user 1 1 1 1
8 access_account_tax_report access.account.tax.report.wizard model_account_tax_report_wizard account.group_account_user 1 1 1 1
9 access_account_financial_report_accountant_bm access.account.financial.report.bmanager model_account_financial_report account.group_account_manager 1 1 1 1
10 access_account_report_general_ledger_bm access.account.report.general.ledger.bmanager model_account_report_general_ledger account.group_account_manager 1 1 1 1
11 access_account_balance_report_bm access.account.balance.report.bmanager model_account_balance_report account.group_account_manager 1 1 1 1
12 access_account_report_partner_ledger_bm access.account.report.partner.ledger.bmanager model_account_report_partner_ledger account.group_account_manager 1 1 1 1
13 access_accounting_report_bm access.accounting.report.bmanager model_accounting_report account.group_account_manager 1 1 1 1
14 access_account_aged_trial_balance_bm access.account.aged.trial.balance.bmanager model_account_aged_trial_balance account.group_account_manager 1 1 1 1
15 access_account_tax_report_bm access.account.tax.report.wizard.bmanager model_account_tax_report_wizard account.group_account_manager 1 1 1 1
16 access_account_print_journal_bm access.account.account.print.journal.bmanager model_account_print_journal account.group_account_manager 1 1 1 1
17 access_account_common_journal_report access.account.common.journal.report model_account_common_journal_report account.group_account_user 1 1 1 0
18 access_account_print_journal access.account.print.journal model_account_print_journal account.group_account_user 1 1 1 0
19 access_account_common_account_report access_account_common_account_report model_account_common_account_report base.group_user 1 0 0 0
20 access_account_common_partner_report access_account_common_partner_report model_account_common_partner_report base.group_user 1 0 0 0
21 access_account_common_report access_account_common_report accounting_pdf_reports.model_account_common_report base.group_user 1 0 0 0
22 access_account_account_type access_account_account_type accounting_pdf_reports.model_account_account_type base.group_user 1 0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,155 @@
<section class="oe_container oe_dark">
<div class="col-md-12">
<h2 class="oe_slogan" style="font-size: 35px;color:#2C0091"><b>Accounting Reports Odoo 18</b></h2>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div style="align:center;">
<h1 style="text-align: center;">
<span align="center" style="color:#148963;">
<span class="fa fa-star fa-spin">
</span>
Added Financial Reports:</span>
</h1>
<div class="row" style="margin-top: 2rem;">
<div class="col-lg-12">
<div class="mt-3">
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">Partner Ledger Report.</span>
</p><br/>
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">Journals Audit.</span>
</p><br/>
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">General Ledger.</span>
</p><br/>
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">Trial Balance.</span>
</p><br/>
</div>
</div>
<div class="col-lg-12">
<div class="mt-3">
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">Aged Partner Balance.</span>
</p><br/>
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">Profit and Loss.</span>
</p><br/>
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">Balance Sheet.</span>
</p><br/>
<p class="fa fa-check" style="color:green;font-size: 15px;">
<span style="color:#000000;font-size: 15px;">Tax Report.</span>
</p><br/>
</div>
</div>
</div>
</div>
<br/>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_centeralign oe_websiteonly">
<h4 class="oe_slogan"><a href="https://www.youtube.com/watch?v=yA4NLwOLZms" target="_blank" style="color: #FFFFFF !important; border-radius: 0; background-color: #9c676e; border-color: #005ca7; padding: 15px; font-weight: bold;">
<i class="fa fa-youtube">
Watch on YouTube
</i>
</a></h4>
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="color:olive;">Accounting Reports</h2>
<h3 class="oe_slogan" style="color:#000066;font-size: 24px;">All in one financial reports for odoo community edition</h3>
<div class="oe_demo oe_picture oe_screenshot">
<img src="account_reports.png" style="height:400px;">
</div>
<br/>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<h3 class="oe_slogan" style="color:#332c3c;font-size: 28px;">General Ledger</h3>
<h3 class="oe_slogan" style="color:#000066;font-size: 24px;">General ledger report with accounts, partners and analytic account filter</h3>
<div class="oe_demo oe_picture oe_screenshot">
<img src="general_ledger_filter.png" style="height:400px;">
</div>
<br/>
<h4 class="oe_slogan" style="color:#332c3c;font-size: 28px;">Report</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="general_ledger_report.png" style="height:400px;">
</div>
</div>
</section>
<section class="oe_container oe_dark">
<div class="oe_row oe_spaced">
<h3 class="oe_slogan" style="color:#1b1d26;">Partner Ledger</h3>
<h3 class="oe_slogan" style="color:#000066;font-size: 24px;">Partner ledger report with partner filter.</h3>
<div class="oe_demo oe_picture oe_screenshot">
<img src="partner_ledger_filter.png" style="height:400px;">
</div>
<br/>
<h4 class="oe_slogan" style="color:#332c3c;font-size: 28px;">Report</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="partner_ledger_report.png" style="height:400px;">
</div>
</div>
</section>
<section class="oe_container">
<div class="oe_row oe_spaced">
<h3 class="oe_slogan" style="color:#1b1d26;">Aged Partner Balance</h3>
<div class="oe_demo oe_picture oe_screenshot">
<img src="aged_partner_balance_filter.png" style="height:400px;">
</div>
<br/>
<h4 class="oe_slogan" style="color:#332c3c;font-size: 28px;">Report</h4>
<div class="oe_demo oe_picture oe_screenshot">
<img src="aged_partner_balance_report.png" style="height:400px;">
</div>
</div>
</section>
<br/>
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
<section class="oe_container oe_dark">
<div class="oe_row ">
<div class="oe_slogan text-center">
<img src="odoo_mates.png"/>
<div style="color:#269900;">
<h3 style="color:#2C0091;font-size: 25px;">If you need any support or want more features, just contact us:</h3><br>
<h3 style="color:#2C0091;font-size: 20px;">Email: <a href="odoomates@gmail.com">odoomates@gmail.com</a> <br></h3>
</div>
<div class="oe_slogan">
<h2>
<a target="_blank" href="https://www.facebook.com/odoomate/" target="new">
<i class="fa fa-facebook-square" style="font-size:38px;"></i>
</a>
<a target="_blank" href="https://twitter.com/odoomates/" target="new">
<i class="fa fa-twitter" style="font-size:38px;"></i>
</a>
<a href="#" target="_blank">
<i class="fa fa-linkedin" style="font-size:38px;"></i>
</a>
<a target="_blank" href="https://www.youtube.com/channel/UCVKlUZP7HAhdQgs-9iTJklQ">
<i class="fa fa-youtube-play" style="font-size:38px;"></i>
</a>
</h2>
</div>
</div>
</div>
</section>
<hr style="width: 100%;height: 4px;background: #148963;margin: 0px 0px;">
<hr style="width: 100%;height: 4px;background: #2C0091;margin: 0px 0px;">

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_account_financial_report_form" model="ir.ui.view">
<field name="name">account.financial.report.form</field>
<field name="model">account.financial.report</field>
<field name="arch" type="xml">
<form string="Account Report">
<sheet>
<group>
<group>
<field name="name"/>
<field name="parent_id"/>
<field name="sequence"/>
</group>
<group>
<field name="type"/>
<field name="sign"/>
<field name="style_overwrite"/>
</group>
</group>
<notebook>
<page string="Report"
invisible="type not in ['accounts', 'account_type', 'account_report']">
<group>
<field name="display_detail"
invisible="type not in ['accounts', 'account_type', 'account_report']"/>
<field name="account_report_id"
invisible="type != 'account_report'"/>
</group>
<field name="account_ids" invisible="type != 'accounts'"/>
<field name="account_type_ids" invisible="type != 'account_type'"/>
</page>
<page string="Childrens">
<field name="children_ids" nolabel="1">
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_account_financial_report_tree" model="ir.ui.view">
<field name="name">account.financial.report.list</field>
<field name="model">account.financial.report</field>
<field name="arch" type="xml">
<list string="Account Report">
<field name="name"/>
<field name="parent_id" invisible="1"/>
<field name="type"/>
<field name="account_report_id"/>
</list>
</field>
</record>
<record id="view_account_financial_report_search" model="ir.ui.view">
<field name="name">account.financial.report.search</field>
<field name="model">account.financial.report</field>
<field name="arch" type="xml">
<search string="Account Report">
<field name="name" string="Account Report"/>
<field name="type"/>
<field name="account_report_id"/>
<filter string="Reports" name="filter_parent_id" domain="[('parent_id','=', False)]"/>
<group>
<filter name="parent_report" string="Parent Report"
context="{'group_by':'parent_id'}"/>
<filter name="report_type" string="Report Type" context="{'group_by':'type'}"/>
</group>
</search>
</field>
</record>
<record id="action_account_financial_report_tree" model="ir.actions.act_window">
<field name="name">Financial Reports</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.financial.report</field>
<field name="view_mode">list,form</field>
<field name="context">{'search_default_filter_parent_id': True}</field>
<field name="search_view_id" ref="view_account_financial_report_search"/>
<field name="view_id" ref="view_account_financial_report_tree"/>
</record>
<menuitem id="menu_finance_reports_settings"
name="Financial Reports"
sequence="9"
groups="account.group_account_user,account.group_account_manager"
parent="account.menu_finance_configuration"/>
<menuitem id="menu_account_reports"
name="Account Reports"
action="action_account_financial_report_tree"
groups="account.group_account_user,account.group_account_manager"
parent="menu_finance_reports_settings"/>
</odoo>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_account_moves_ledger_general" model="ir.actions.act_window">
<field name="context">{'journal_type':'general', 'search_default_group_by_account': 1, 'search_default_posted':1}</field>
<field name="name">General Ledger</field>
<field name="res_model">account.move.line</field>
<field name="domain">[('display_type', 'not in', ('line_section', 'line_note'))]</field>
<field name="view_id" ref="account.view_move_line_tree_grouped_general"/>
<field name="search_view_id" ref="account.view_account_move_line_filter"/>
<field name="view_mode">list,pivot,graph</field>
</record>
<record id="action_account_moves_ledger_partner" model="ir.actions.act_window">
<field name="context">{'journal_type':'general', 'search_default_group_by_partner': 1,
'search_default_posted':1, 'search_default_payable':1, 'search_default_receivable':1,
'search_default_unreconciled':1}
</field>
<field name="name">Partner Ledger</field>
<field name="res_model">account.move.line</field>
<field name="domain">[('display_type', 'not in', ('line_section', 'line_note'))]</field>
<field name="view_id" ref="account.view_move_line_tree_grouped_partner"/>
<field name="search_view_id" ref="account.view_account_move_line_filter"/>
<field name="view_mode">list,pivot,graph</field>
</record>
<menuitem id="menu_finance_entries_accounting_ledgers" name="Ledgers" parent="account.menu_finance_entries"
sequence="3">
<menuitem id="menu_action_account_moves_ledger_general" action="action_account_moves_ledger_general"
groups="account.group_account_readonly" sequence="1"/>
<menuitem id="menu_action_account_moves_ledger_partner" action="action_account_moves_ledger_partner"
groups="account.group_account_readonly" sequence="2"/>
</menuitem>
</odoo>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem id="menu_finance_legal_statement"
name="Financial Reports"
sequence="10"
parent="account.menu_finance_reports"/>
<menuitem id="menu_finance_partner_reports"
name="Partner Reports"
sequence="20"
parent="account.menu_finance_reports"/>
<menuitem id="menu_finance_audit_reports"
name="Audit Reports"
sequence="30"
parent="account.menu_finance_reports"/>
<record id="account.account_reports_management_menu" model="ir.ui.menu">
<field name="sequence" eval="40"/>
</record>
</odoo>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.accountant</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
<field name="arch" type="xml">
<app name="account" position="inside">
<h2>Enhanced Financial Reports</h2>
<div>
<div class="row mt16 o_settings_container" name="report_setting_container">
<div class="col-6 col-lg-6 o_setting_box" id="enhanced_reports">
<div>
Preview financial reports without downloading
</div>
<div class="content-group">
<a target="_blank" href="https://apps.odoo.com/apps/modules/19.0/om_accounting_reports/"
style="text-decoration: underline;">Enhanced Financial Reports</a>
</div>
</div>
<div class="col-6 col-lg-6 o_setting_box" id="excel_reports">
<div>
Financial Reports in Excel
</div>
<div class="content-group">
<a target="_blank" href="https://apps.odoo.com/apps/modules/19.0/accounting_excel_reports/"
style="text-decoration: underline;">Excel Reports</a>
</div>
</div>
</div>
</div>
</app>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,12 @@
from . import account_report_common
from . import account_report_common_journal
from . import account_report_print_journal
from . import account_report
from . import account_report_common_partner
from . import account_report_common_account
from . import account_partner_ledger
from . import account_general_ledger
from . import account_trial_balance
from . import account_tax_report
from . import aged_partner
from . import account_journal_audit

View File

@@ -0,0 +1,35 @@
from odoo import fields, models, api, _
from odoo.exceptions import UserError
class AccountReportGeneralLedger(models.TransientModel):
_name = "account.report.general.ledger"
_inherit = "account.common.account.report"
_description = "General Ledger Report"
initial_balance = fields.Boolean(
string='Include Initial Balances',
help='If you selected date, this field allow you to add a row '
'to display the amount of debit/credit/balance that precedes '
'the filter you have set.'
)
sortby = fields.Selection(
[('sort_date', 'Date'), ('sort_journal_partner', 'Journal & Partner')],
string='Sort by', required=True, default='sort_date'
)
journal_ids = fields.Many2many(
'account.journal', 'account_report_general_ledger_journal_rel',
'account_id', 'journal_id', string='Journals', required=True
)
def _get_report_data(self, data):
data = self.pre_print_report(data)
data['form'].update(self.read(['initial_balance', 'sortby'])[0])
if data['form'].get('initial_balance') and not data['form'].get('date_from'):
raise UserError(_("You must define a Start Date"))
records = self.env[data['model']].browse(data.get('ids', []))
return records, data
def _print_report(self, data):
records, data = self._get_report_data(data)
return self.env.ref('accounting_pdf_reports.action_report_general_ledger').with_context(landscape=True).report_action(records, data=data)

View File

@@ -0,0 +1,21 @@
from odoo import fields, models, api
class AccountPrintJournal(models.TransientModel):
_name = "account.print.journal"
_inherit = "account.common.journal.report"
_description = "Account Print Journal"
sort_selection = fields.Selection([('date', 'Date'), ('move_name', 'Journal Entry Number')],
'Entries Sorted by', required=True, default='move_name')
journal_ids = fields.Many2many('account.journal', string='Journals', required=True,
default=lambda self: self.env['account.journal'].search([('type', 'in', ['sale', 'purchase'])]))
def _get_report_data(self, data):
data = self.pre_print_report(data)
data['form'].update({'sort_selection': self.sort_selection})
return data
def _print_report(self, data):
data = self._get_report_data(data)
return self.env.ref('accounting_pdf_reports.action_report_journal').with_context(landscape=True).report_action(self, data=data)

View File

@@ -0,0 +1,24 @@
from odoo import fields, models, api, _
class AccountPartnerLedger(models.TransientModel):
_name = "account.report.partner.ledger"
_inherit = "account.common.partner.report"
_description = "Account Partner Ledger"
amount_currency = fields.Boolean("With Currency",
help="It adds the currency column on "
"report if the currency differs from "
"the company currency.")
reconciled = fields.Boolean('Reconciled Entries')
def _get_report_data(self, data):
data = self.pre_print_report(data)
data['form'].update({'reconciled': self.reconciled,
'amount_currency': self.amount_currency})
return data
def _print_report(self, data):
data = self._get_report_data(data)
return self.env.ref('accounting_pdf_reports.action_report_partnerledger').with_context(landscape=True).\
report_action(self, data=data)

View File

@@ -0,0 +1,55 @@
from odoo import api, fields, models
class AccountingReport(models.TransientModel):
_name = "accounting.report"
_inherit = "account.common.report"
_description = "Accounting Report"
@api.model
def _get_account_report(self):
reports = []
if self.env.context.get('active_id'):
menu = self.env['ir.ui.menu'].browse(self.env.context.get('active_id')).name
reports = self.env['account.financial.report'].search([('name', 'ilike', menu)])
return reports and reports[0] or False
enable_filter = fields.Boolean(string='Enable Comparison')
account_report_id = fields.Many2one('account.financial.report', string='Account Reports',
required=True, default=_get_account_report)
label_filter = fields.Char(string='Column Label', help="This label will be displayed on report to "
"show the balance computed for the given comparison filter.")
filter_cmp = fields.Selection([('filter_no', 'No Filters'), ('filter_date', 'Date')],
string='Filter by', required=True, default='filter_no')
date_from_cmp = fields.Date(string='Date From')
date_to_cmp = fields.Date(string='Date To')
debit_credit = fields.Boolean(string='Display Debit/Credit Columns',
help="This option allows you to get more details about "
"the way your balances are computed."
" Because it is space consuming, we do not allow to"
" use it while doing a comparison.")
def _build_comparison_context(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
if data['form']['filter_cmp'] == 'filter_date':
result['date_from'] = data['form']['date_from_cmp']
result['date_to'] = data['form']['date_to_cmp']
result['strict_range'] = True
return result
def check_report(self):
res = super(AccountingReport, self).check_report()
data = {}
data['form'] = self.read(['account_report_id', 'date_from_cmp', 'date_to_cmp', 'journal_ids', 'filter_cmp', 'target_move'])[0]
for field in ['account_report_id']:
if isinstance(data['form'][field], tuple):
data['form'][field] = data['form'][field][0]
comparison_context = self._build_comparison_context(data)
res['data']['form']['comparison_context'] = comparison_context
return res
def _print_report(self, data):
data['form'].update(self.read(['date_from_cmp', 'debit_credit', 'date_to_cmp', 'filter_cmp', 'account_report_id', 'enable_filter', 'label_filter', 'target_move'])[0])
return self.env.ref('accounting_pdf_reports.action_report_financial').report_action(self, data=data, config=False)

View File

@@ -0,0 +1,52 @@
from odoo import api, fields, models, _
from odoo.tools.misc import get_lang
class AccountCommonReport(models.TransientModel):
_name = "account.common.report"
_description = "Account Common Report"
company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True, default=lambda self: self.env.company)
journal_ids = fields.Many2many(
comodel_name='account.journal',
string='Journals',
required=True,
default=lambda self: self.env['account.journal'].search([('company_id', '=', self.company_id.id)]),
domain="[('company_id', '=', company_id)]",
)
date_from = fields.Date(string='Start Date')
date_to = fields.Date(string='End Date')
target_move = fields.Selection([('posted', 'All Posted Entries'),
('all', 'All Entries'),
], string='Target Moves', required=True, default='posted')
@api.onchange('company_id')
def _onchange_company_id(self):
if self.company_id:
self.journal_ids = self.env['account.journal'].search(
[('company_id', '=', self.company_id.id)])
else:
self.journal_ids = self.env['account.journal'].search([])
def _build_contexts(self, data):
result = {}
result['journal_ids'] = 'journal_ids' in data['form'] and data['form']['journal_ids'] or False
result['state'] = 'target_move' in data['form'] and data['form']['target_move'] or ''
result['date_from'] = data['form']['date_from'] or False
result['date_to'] = data['form']['date_to'] or False
result['strict_range'] = True if result['date_from'] else False
result['company_id'] = data['form']['company_id'][0] or False
return result
def _print_report(self, data):
raise NotImplementedError()
def check_report(self):
self.ensure_one()
data = {}
data['ids'] = self.env.context.get('active_ids', [])
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
data['form'] = self.read(['date_from', 'date_to', 'journal_ids', 'target_move', 'company_id'])[0]
used_context = self._build_contexts(data)
data['form']['used_context'] = dict(used_context, lang=get_lang(self.env).code)
return self.with_context(discard_logo_check=True)._print_report(data)

View File

@@ -0,0 +1,26 @@
from odoo import api, fields, models
class AccountCommonAccountReport(models.TransientModel):
_name = 'account.common.account.report'
_inherit = "account.common.report"
_description = 'Account Common Account Report'
display_account = fields.Selection([('all', 'All'),
('movement', 'With movements'),
('not_zero', 'With balance is not equal to 0'), ],
string='Display Accounts',
required=True, default='movement')
analytic_account_ids = fields.Many2many('account.analytic.account',
string='Analytic Accounts')
account_ids = fields.Many2many('account.account', string='Accounts')
partner_ids = fields.Many2many('res.partner', string='Partners')
def pre_print_report(self, data):
data['form'].update(self.read(['display_account'])[0])
data['form'].update({
'analytic_account_ids': self.analytic_account_ids.ids,
'partner_ids': self.partner_ids.ids,
'account_ids': self.account_ids.ids,
})
return data

View File

@@ -0,0 +1,13 @@
from odoo import api, fields, models
class AccountCommonJournalReport(models.TransientModel):
_name = 'account.common.journal.report'
_description = 'Common Journal Report'
_inherit = "account.common.report"
amount_currency = fields.Boolean('With Currency', help="Print Report with the currency column if the currency differs from the company currency.")
def pre_print_report(self, data):
data['form'].update({'amount_currency': self.amount_currency})
return data

View File

@@ -0,0 +1,18 @@
from odoo import fields, models
class AccountingCommonPartnerReport(models.TransientModel):
_name = 'account.common.partner.report'
_inherit = "account.common.report"
_description = 'Account Common Partner Report'
result_selection = fields.Selection([('customer', 'Receivable Accounts'),
('supplier', 'Payable Accounts'),
('customer_supplier', 'Receivable and Payable Accounts')
], string="Partner's", required=True, default='customer')
partner_ids = fields.Many2many('res.partner', string='Partners')
def pre_print_report(self, data):
data['form'].update(self.read(['result_selection'])[0])
data['form'].update({'partner_ids': self.partner_ids.ids})
return data

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_common_report_view" model="ir.ui.view">
<field name="name">Common Report</field>
<field name="model">account.common.report</field>
<field name="arch" type="xml">
<form string="Report Options">
<group col="4">
<field name="target_move" widget="radio"/>
<field name="date_from"/>
<field name="date_to"/>
</group>
<group>
<field name="journal_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="company_id" invisible="1"/>
</group>
<footer>
<button name="check_report" string="Print" type="object" default_focus="1" class="oe_highlight" data-hotkey="q"/>
<button string="Cancel" class="btn btn-secondary" special="cancel" data-hotkey="z" />
</footer>
</form>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,21 @@
from odoo import fields, models
class AccountPrintJournal(models.TransientModel):
_inherit = "account.common.journal.report"
_name = "account.print.journal"
_description = "Account Print Journal"
sort_selection = fields.Selection(
[('date', 'Date'), ('move_name', 'Journal Entry Number')],
'Entries Sorted by', required=True, default='move_name'
)
journal_ids = fields.Many2many(
'account.journal', string='Journals', required=True,
default=lambda self: self.env['account.journal'].search([('type', 'in', ['sale', 'purchase'])])
)
def _print_report(self, data):
data = self.pre_print_report(data)
data['form'].update({'sort_selection': self.sort_selection})
return self.env.ref('account.action_report_journal').with_context(landscape=True).report_action(self, data=data)

View File

@@ -0,0 +1,20 @@
from odoo import models, api, fields
from datetime import date
class AccountTaxReport(models.TransientModel):
_name = 'account.tax.report.wizard'
_inherit = "account.common.report"
_description = 'Tax Report'
date_from = fields.Date(
string='Date From', required=True,
default=lambda self: fields.Date.to_string(date.today().replace(day=1))
)
date_to = fields.Date(
string='Date To', required=True,
default=lambda self: fields.Date.to_string(date.today())
)
def _print_report(self, data):
return self.env.ref('accounting_pdf_reports.action_report_account_tax').report_action(self, data=data)

View File

@@ -0,0 +1,26 @@
from odoo import fields, models, api
class AccountBalanceReport(models.TransientModel):
_name = 'account.balance.report'
_inherit = "account.common.account.report"
_description = 'Trial Balance Report'
journal_ids = fields.Many2many(
'account.journal', 'account_balance_report_journal_rel',
'account_id', 'journal_id',
string='Journals', required=True, default=[]
)
analytic_account_ids = fields.Many2many(
'account.analytic.account',
'account_trial_balance_analytic_rel', string='Analytic Accounts'
)
def _get_report_data(self, data):
data = self.pre_print_report(data)
records = self.env[data['model']].browse(data.get('ids', []))
return records, data
def _print_report(self, data):
records, data = self._get_report_data(data)
return self.env.ref('accounting_pdf_reports.action_report_trial_balance').report_action(records, data=data)

View File

@@ -0,0 +1,41 @@
import time
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class AccountAgedTrialBalance(models.TransientModel):
_name = 'account.aged.trial.balance'
_inherit = 'account.common.partner.report'
_description = 'Account Aged Trial balance Report'
period_length = fields.Integer(string='Period Length (days)', required=True, default=30)
journal_ids = fields.Many2many('account.journal', string='Journals', required=True)
date_from = fields.Date(default=lambda *a: time.strftime('%Y-%m-%d'))
def _get_report_data(self, data):
res = {}
data = self.pre_print_report(data)
data['form'].update(self.read(['period_length'])[0])
period_length = data['form']['period_length']
if period_length <= 0:
raise UserError(_('You must set a period length greater than 0.'))
if not data['form']['date_from']:
raise UserError(_('You must set a start date.'))
start = data['form']['date_from']
for i in range(5)[::-1]:
stop = start - relativedelta(days=period_length - 1)
res[str(i)] = {
'name': (i != 0 and (str((5 - (i + 1)) * period_length) + '-' + str((5 - i) * period_length)) or (
'+' + str(4 * period_length))),
'stop': start.strftime('%Y-%m-%d'),
'start': (i != 0 and stop.strftime('%Y-%m-%d') or False),
}
start = stop - relativedelta(days=1)
data['form'].update(res)
return data
def _print_report(self, data):
data = self._get_report_data(data)
return self.env.ref('accounting_pdf_reports.action_report_aged_partner_balance').\
with_context(landscape=True).report_action(self, data=data)

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_aged_balance_view" model="ir.ui.view">
<field name="name">Aged Partner Balance</field>
<field name="model">account.aged.trial.balance</field>
<field name="arch" type="xml">
<form string="Report Options">
<group col="4">
<field name="date_from"/>
<field name="period_length"/>
<field name="company_id" invisible="1"/>
<newline/>
<field name="result_selection" widget="radio"
invisible="context.get('hide_result_selection')"/>
<field name="target_move" widget="radio"/>
</group>
<field name="journal_ids" required="0" invisible="1"/>
<xpath expr="//field[@name='journal_ids']" position="before">
<group>
<field name="partner_ids" widget="many2many_tags"
options="{'no_open': True, 'no_create': True}"/>
</group>
</xpath>
<footer>
<button name="check_report" class="oe_highlight"
string="Print" type="object"/>
<button string="Cancel" class="btn btn-default" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_account_aged_balance_view" model="ir.actions.act_window">
<field name="name">Aged Partner Balance</field>
<field name="res_model">account.aged.trial.balance</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="account_aged_balance_view"/>
<field name="context"></field>
<field name="target">new</field>
</record>
<menuitem id="menu_aged_trial_balance"
name="Aged Partner Balance"
sequence="10"
action="action_account_aged_balance_view"
parent="menu_finance_partner_reports"/>
<record id="action_account_aged_receivable" model="ir.actions.act_window">
<field name="name">Aged Receivable</field>
<field name="res_model">account.aged.trial.balance</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="account_aged_balance_view"/>
<field name="context">{'default_result_selection': 'customer',
'hide_result_selection': 1}</field>
<field name="target">new</field>
</record>
<menuitem id="menu_aged_receivable"
name="Aged Receivable"
sequence="20"
action="action_account_aged_receivable"
parent="menu_finance_partner_reports"/>
<record id="action_account_aged_payable" model="ir.actions.act_window">
<field name="name">Aged Payable</field>
<field name="res_model">account.aged.trial.balance</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="account_aged_balance_view"/>
<field name="context">{'default_result_selection': 'supplier',
'hide_result_selection': 1}</field>
<field name="target">new</field>
</record>
<menuitem id="menu_aged_payable"
name="Aged Payable"
sequence="30"
action="action_account_aged_payable"
parent="menu_finance_partner_reports"/>
</odoo>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_financial_report_profitandloss0" model="account.financial.report">
<field name="name">Profit and Loss</field>
<field name="sign">-1</field>
<field name="type">sum</field>
</record>
<record id="account_financial_report_income0" model="account.financial.report">
<field name="name">Income</field>
<field name="sign">-1</field>
<field name="parent_id" ref="account_financial_report_profitandloss0"/>
<field name="display_detail">detail_with_hierarchy</field>
<field name="type">account_type</field>
<field name="account_type_ids" eval="[(4,ref('accounting_pdf_reports.data_account_type_other_income')), (4,ref('accounting_pdf_reports.data_account_type_revenue'))]"/>
</record>
<record id="account_financial_report_expense0" model="account.financial.report">
<field name="name">Expense</field>
<field name="sign">-1</field>
<field name="parent_id" ref="account_financial_report_profitandloss0"/>
<field name="display_detail">detail_with_hierarchy</field>
<field name="type">account_type</field>
<field name="account_type_ids" eval="[(4,ref('accounting_pdf_reports.data_account_type_expenses')),(4,ref('accounting_pdf_reports.data_account_type_direct_costs')), (4,ref('accounting_pdf_reports.data_account_type_depreciation'))]"/>
</record>
<record id="account_financial_report_balancesheet0" model="account.financial.report">
<field name="name">Balance Sheet</field>
<field name="type">sum</field>
</record>
<record id="account_financial_report_assets0" model="account.financial.report">
<field name="name">Assets</field>
<field name="parent_id" ref="account_financial_report_balancesheet0"/>
<field name="display_detail">detail_with_hierarchy</field>
<field name="type">account_type</field>
<field name="account_type_ids" eval="[(4,ref('accounting_pdf_reports.data_account_type_receivable')),
(4,ref('accounting_pdf_reports.data_account_type_liquidity')), (4,ref('accounting_pdf_reports.data_account_type_current_assets')),
(4,ref('accounting_pdf_reports.data_account_type_non_current_assets'), (4,ref('accounting_pdf_reports.data_account_type_prepayments'))),
(4,ref('accounting_pdf_reports.data_account_type_fixed_assets'))]"/>
</record>
<record id="account_financial_report_liabilitysum0" model="account.financial.report">
<field name="name">Liability</field>
<field name="parent_id" ref="account_financial_report_balancesheet0"/>
<field name="display_detail">no_detail</field>
<field name="type">sum</field>
</record>
<record id="account_financial_report_liability0" model="account.financial.report">
<field name="name">Liability</field>
<field name="parent_id" ref="account_financial_report_liabilitysum0"/>
<field name="display_detail">detail_with_hierarchy</field>
<field name="type">account_type</field>
<field name="account_type_ids" eval="[(4,ref('accounting_pdf_reports.data_account_type_payable')),
(4,ref('accounting_pdf_reports.data_account_type_equity')), (4,ref('accounting_pdf_reports.data_account_type_current_liabilities')),
(4,ref('accounting_pdf_reports.data_account_type_non_current_liabilities'))]"/>
</record>
<record id="account_financial_report_profitloss_toreport0" model="account.financial.report">
<field name="name">Profit (Loss) to report</field>
<field name="parent_id" ref="account_financial_report_liabilitysum0"/>
<field name="display_detail">no_detail</field>
<field name="type">account_report</field>
<field name="account_report_id" ref="account_financial_report_profitandloss0"/>
</record>
<record id="accounting_report_view" model="ir.ui.view">
<field name="name">Accounting Report</field>
<field name="model">accounting.report</field>
<field name="inherit_id" ref="accounting_pdf_reports.account_common_report_view"/>
<field name="arch" type="xml">
<field name="target_move" position="before">
<field name="account_report_id" domain="[('parent_id','=',False)]"/>
</field>
<field name="target_move" position="after">
<field name="enable_filter"/>
<field name="debit_credit" invisible="enable_filter == True"/>
</field>
<field name="journal_ids" position="after">
<notebook tabpos="up" colspan="4">
<page string="Comparison" name="comparison" invisible="enable_filter == False">
<group>
<field name="label_filter" required="enable_filter == True"/>
<field name="filter_cmp"/>
</group>
<group string="Dates" invisible="filter_cmp != 'filter_date'">
<field name="date_from_cmp" required="filter_cmp == 'filter_date'"/>
<field name="date_to_cmp" required="filter_cmp == 'filter_date'"/>
</group>
</page>
</notebook>
</field>
</field>
</record>
<record id="action_account_report_bs" model="ir.actions.act_window">
<field name="name">Balance Sheet</field>
<field name="res_model">accounting.report</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="accounting_report_view"/>
<field name="target">new</field>
<field name="context" eval="{'default_account_report_id':ref('accounting_pdf_reports.account_financial_report_balancesheet0')}"/>
</record>
<menuitem id="menu_account_report_bs"
name="Balance Sheet"
sequence="5"
action="action_account_report_bs"
parent="menu_finance_legal_statement"
groups="account.group_account_user,account.group_account_manager"/>
</odoo>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_report_general_ledger_view" model="ir.ui.view">
<field name="name">General Ledger</field>
<field name="model">account.report.general.ledger</field>
<field name="inherit_id" ref="accounting_pdf_reports.account_common_report_view"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='journal_ids']" position="after">
<field name="analytic_account_ids" widget="many2many_tags"
options="{'no_open': True, 'no_create': True}"
invisible="1"
groups="analytic.group_analytic_accounting"/>
<field name="account_ids" widget="many2many_tags"
options="{'no_open': True, 'no_create': True}"/>
<field name="partner_ids" widget="many2many_tags"
options="{'no_open': True, 'no_create': True}"/>
</xpath>
<xpath expr="//field[@name='target_move']" position="after">
<field name="sortby" widget="radio"/>
<field name="display_account" widget="radio"/>
<field name="initial_balance"/>
<newline/>
</xpath>
</data>
</field>
</record>
<record id="action_account_general_ledger_menu" model="ir.actions.act_window">
<field name="name">General Ledger</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.report.general.ledger</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_general_ledger_view"/>
<field name="target">new</field>
<field name="binding_model_id" ref="account.model_account_account" />
<field name="binding_type">report</field>
</record>
<menuitem id="menu_general_ledger"
name="General Ledger"
sequence="10"
parent="menu_finance_audit_reports"
action="action_account_general_ledger_menu"
groups="account.group_account_user,account.group_account_manager"/>
</odoo>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_report_print_journal_view" model="ir.ui.view">
<field name="name">Journals Audit</field>
<field name="model">account.print.journal</field>
<field name="inherit_id" ref="accounting_pdf_reports.account_common_report_view"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='target_move']" position="after">
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="sort_selection" widget="radio"/>
<newline/>
</xpath>
</data>
</field>
</record>
<record id="action_account_print_journal_menu" model="ir.actions.act_window">
<field name="name">Journals Audit</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.print.journal</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_print_journal_view"/>
<field name="target">new</field>
</record>
<menuitem id="menu_print_journal"
name="Journals Audit"
sequence="40"
parent="menu_finance_audit_reports"
action="action_account_print_journal_menu"
groups="account.group_account_manager,account.group_account_user"/>
</odoo>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_report_partner_ledger_view" model="ir.ui.view">
<field name="name">Partner Ledger</field>
<field name="model">account.report.partner.ledger</field>
<field name="inherit_id" ref="accounting_pdf_reports.account_common_report_view"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='journal_ids']" position="before">
<field name="partner_ids" widget="many2many_tags"
options="{'no_open': True, 'no_create': True}"/>
</xpath>
<xpath expr="//field[@name='target_move']" position="after">
<field name="result_selection"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<newline/>
<field name="reconciled"/>
<newline/>
</xpath>
</data>
</field>
</record>
<record id="action_account_partner_ledger_menu" model="ir.actions.act_window">
<field name="name">Partner Ledger</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.report.partner.ledger</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_partner_ledger_view"/>
<field name="target">new</field>
<field name="binding_model_id" ref="account.model_account_account" />
<field name="binding_type">report</field>
</record>
<menuitem id="menu_partner_ledger"
name="Partner Ledger"
sequence="5"
parent="menu_finance_partner_reports"
action="action_account_partner_ledger_menu"
groups="account.group_account_invoice"/>
<!-- Add to Partner Print button -->
<record id="action_partner_report_partnerledger" model="ir.actions.act_window">
<field name="name">Balance Statement (Partner Ledger)</field>
<field name="res_model">account.report.partner.ledger</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_partner_ledger_view" />
<field name="target">new</field>
<field name="binding_model_id" ref="base.model_res_partner" />
<field name="binding_type">report</field>
<field name="context">{
'default_partner_ids':active_ids,
'default_target_move': 'posted',
'default_result_selection': 'customer_supplier',
'default_reconciled': True,
'hide_partner':1,
}</field>
<field name="group_ids" eval="[(4, ref('account.group_account_invoice'))]"/>
</record>
</odoo>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_account_report_pl" model="ir.actions.act_window">
<field name="name">Profit and Loss</field>
<field name="res_model">accounting.report</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="accounting_report_view"/>
<field name="target">new</field>
<field name="context" eval="{'default_account_report_id':ref('accounting_pdf_reports.account_financial_report_profitandloss0')}"/>
</record>
<menuitem id="menu_account_report_pl"
name="Profit and Loss"
sequence="6"
action="action_account_report_pl"
parent="accounting_pdf_reports.menu_finance_legal_statement"
groups="account.group_account_user,account.group_account_manager"/>
</odoo>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="accounting_tax_report_view" model="ir.ui.view">
<field name="name">Tax Reports</field>
<field name="model">account.tax.report.wizard</field>
<field name="inherit_id" eval="False"/>
<field name="arch" type="xml">
<form string="Report Options">
<group>
<group>
<field name="target_move" widget="radio"/>
<field name="date_from"/>
</group>
<group>
<field name="company_id" invisible="1"/>
<field name="date_to" />
</group>
</group>
<footer>
<button name="check_report" string="Print" type="object" default_focus="1" class="oe_highlight" data-hotkey="q"/>
<button string="Cancel" class="btn btn-secondary" special="cancel" data-hotkey="z"/>
</footer>
</form>
</field>
</record>
<record id="action_account_tax_report" model="ir.actions.act_window">
<field name="name">Tax Reports</field>
<field name="res_model">account.tax.report.wizard</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">form</field>
<field name="view_id" ref="accounting_tax_report_view"/>
<field name="context">{}</field>
<field name="target">new</field>
</record>
<menuitem id="menu_account_report"
name="Tax Report"
sequence="30"
action="action_account_tax_report"
parent="menu_finance_audit_reports"
groups="account.group_account_manager,account.group_account_user"/>
</odoo>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_report_balance_view" model="ir.ui.view">
<field name="name">Trial Balance</field>
<field name="model">account.balance.report</field>
<field name="inherit_id" ref="accounting_pdf_reports.account_common_report_view"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='target_move']" position="after">
<field name="display_account" widget="radio"/>
<newline/>
</xpath>
<xpath expr="//field[@name='journal_ids']" position="after">
<field name="analytic_account_ids" widget="many2many_tags"
invisible="1"
options="{'no_open': True, 'no_create': True}"/>
</xpath>
</data>
</field>
</record>
<record id="action_account_balance_menu" model="ir.actions.act_window">
<field name="name">Trial Balance</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.balance.report</field>
<field name="view_mode">form</field>
<field name="view_id" ref="account_report_balance_view"/>
<field name="target">new</field>
<field name="binding_model_id" ref="account.model_account_account" />
<field name="binding_type">report</field>
</record>
<menuitem id="menu_general_balance_report"
name="Trial Balance"
sequence="20"
parent="menu_finance_audit_reports"
action="action_account_balance_menu"
groups="account.group_account_user,account.group_account_manager"/>
</odoo>

View File

@@ -0,0 +1,2 @@
from . import models
from . import wizard

View File

@@ -0,0 +1,151 @@
{
'name': 'Laundry Management',
'version': '19.0.19.0.4',
'summary': 'Laundry Management',
'description': 'Laundry Management',
'author': 'Laundry Management',
'category': 'Services',
'license': 'LGPL-3',
'depends': [
'base',
'mail',
'sale', # sale.order (legacy refs in commission/dashboard)
'account', # account.move, account.payment (legacy refs)
'product', # product.template, product.product
'base_setup', # res.config.settings integration
'sales_team', # group_sale_salesman, group_sale_manager
'point_of_sale', # pos.order ??? source of truth
],
'data': [
# 1. Security ??? groups must load before ACLs and rules
'security/res_groups.xml',
'security/ir_rule.xml',
'security/ir.model.access.csv',
# 2. Sequences
'data/sequence.xml',
# 3. Master data
'data/laundry_data.xml',
'data/service_catalog_data.xml',
# 4. Reports ??? must load before views that reference report actions
'report/laundry_order_report.xml',
'report/laundry_thermal_report.xml',
'report/laundry_work_order_report.xml',
# 5. Action-defining views ??? must load before menus + reporting,
# because both reference these actions by xml id.
'views/product_template_views.xml',
'views/laundry_order_type_views.xml',
'views/laundry_order_attribute_views.xml',
'views/laundry_order_views.xml',
'views/pos_order_views.xml',
'views/laundry_commission_views.xml',
# 6. Configuration views
'views/laundry_payment_method_views.xml',
'views/laundry_settings_views.xml',
'views/pos_config_views.xml',
# 7. Wizards (their actions are referenced from menus / forms)
'views/laundry_print_wizard_views.xml',
'wizard/laundry_order_unlock_wizard_views.xml',
# 8. MENUS ??? must load BEFORE any file that adds a child menu
# under menu_laundry_root (e.g. laundry_reporting_views.xml).
'views/laundry_menus.xml',
# 9. Reporting ??? adds child menus under menu_laundry_root
# (defined in the file above) and under core
# sale.menu_sale_report / account.menu_finance_reports.
'views/laundry_reporting_views.xml',
],
'assets': {
# ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
# POS asset bundle ??? EXPLICIT FILE LIST (no broad globs).
# ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
# Lists only the production-required POS workflow files.
# Suspect / experimental files are commented OUT individually
# rather than disabling the whole bundle. Toggle a single line
# to add or remove a feature.
#
# The defensive guard for the `doHaveConflictWith` crash lives
# in pos_store_patch.js ??? that crash is fixed without removing
# any feature.
# ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
'point_of_sale._assets_pos': [
# ?????? Styling (shared with backend kanban) ??????????????????????????????????????????????????????
'laundry_management/static/src/scss/laundry_pos.scss',
# ?????? Reactive store (must load first; other patches use it)
'laundry_management/static/src/js/laundry_context_store.js',
# ?????? Model + screen patches (workflow core) ????????????????????????????????????????????????
'laundry_management/static/src/js/pos_order_patch.js',
'laundry_management/static/src/js/pos_store_patch.js',
'laundry_management/static/src/js/payment_screen_patch.js',
'laundry_management/static/src/js/order_payment_validation.js',
# ?????? Settle-Due (production-required) ??????????????????????????????????????????????????????????????????
'laundry_management/static/src/js/settle_dues.js',
'laundry_management/static/src/js/settlement_receipt.js',
'laundry_management/static/src/js/laundry_settle_banner.js',
'laundry_management/static/src/js/closing_popup_patch.js',
# ?????? Customer Laundry Orders popup (production-required) ??????
'laundry_management/static/src/js/view_laundry_orders.js',
'laundry_management/static/src/js/quick_create_partner.js',
# ?????? Legacy order-type / attribute / delivery flow ???????????????????????????
'laundry_management/static/src/js/laundry_order_context_panel.js',
'laundry_management/static/src/js/popups/laundry_delivery_details_popup.js',
'laundry_management/static/src/js/popups/laundry_order_attribute_popup.js',
'laundry_management/static/src/js/popups/laundry_order_type_popup.js',
# ?????? Cashier UI helpers ????????????????????????????????????????????????????????????????????????????????????????????????????????????
'laundry_management/static/src/js/control_buttons_patch.js',
'laundry_management/static/src/js/navbar_patch.js',
'laundry_management/static/src/js/order_summary_patch.js',
'laundry_management/static/src/js/order_tabs_patch.js',
'laundry_management/static/src/js/ticket_screen_patch.js',
'laundry_management/static/src/js/order_receipt_patch.js',
'laundry_management/static/src/js/laundry_receipt_details.js',
'laundry_management/static/src/js/laundry_pricing_hook.js',
# ?????? XML templates for the JS files above ??????????????????????????????????????????????????????
'laundry_management/static/src/xml/closing_popup_ext.xml',
'laundry_management/static/src/xml/control_buttons.xml',
'laundry_management/static/src/xml/laundry_order_context_panel.xml',
'laundry_management/static/src/xml/laundry_settle_banner.xml',
'laundry_management/static/src/xml/order_summary_patch.xml',
'laundry_management/static/src/xml/partner_line.xml',
'laundry_management/static/src/xml/popups/laundry_delivery_details_popup.xml',
'laundry_management/static/src/xml/popups/laundry_order_attribute_popup.xml',
'laundry_management/static/src/xml/popups/laundry_order_type_popup.xml',
'laundry_management/static/src/xml/quick_create_partner.xml',
'laundry_management/static/src/xml/receipt.xml',
'laundry_management/static/src/xml/settle_dues.xml',
'laundry_management/static/src/xml/settlement_receipt.xml',
'laundry_management/static/src/xml/view_laundry_orders.xml',
# ?????? Improved laundry configurator UX (XML-only) ?????????????????????????????????
# Pure XML inheritance on Odoo's ProductConfiguratorPopup
# that adds two CSS classes to the Dialog's contentClass so
# the existing SCSS in laundry_pos.scss enhances the popup
# for laundry-flagged products only. NO JS override, NO
# logic change. The defensive doHaveConflictWith guard in
# pos_store_patch.js handles the data-shape edge case
# independently ??? re-enabling this is safe.
'laundry_management/static/src/xml/product_configurator_popup.xml',
# ?????? STILL EXCLUDED ??? recent / experimental ????????????????????????????????????????????????
# Thermal-receipt component is kept off until an explicit
# printer-side validation. PDF fallback remains the path.
# 'laundry_management/static/src/js/laundry_thermal_receipt.js',
# 'laundry_management/static/src/xml/laundry_thermal_receipt.xml',
],
'web.assets_backend': [
# SCSS shared with backend kanban / dashboard styling.
'laundry_management/static/src/scss/laundry_pos.scss',
],
},
'demo': [
'demo/demo.xml',
],
'installable': True,
'application': True,
'auto_install': False,
}

View File

@@ -0,0 +1,349 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- ═══════════════════════════════════════════════════════════════
SETTLEMENT PRODUCT — used for collecting outstanding laundry dues.
Service type, no tax, NOT a laundry service (no laundry.order created).
The income account override happens in pos.order.line Python code
to use the partner's receivable account → no revenue recognised.
═══════════════════════════════════════════════════════════════ -->
<record id="product_laundry_settlement" model="product.product">
<field name="name">Laundry Settlement</field>
<field name="type">service</field>
<field name="lst_price">0.00</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="available_in_pos">True</field>
<field name="is_laundry_settlement">True</field>
<field name="is_laundry_service">False</field>
<field name="taxes_id" eval="[(5, 0, 0)]"/>
<field name="description_sale">Settlement of outstanding laundry dues.</field>
</record>
<!-- ═══════════════════════════════════════════════════════════════
PRODUCT CATEGORIES (Laundry Services tree)
═══════════════════════════════════════════════════════════════ -->
<record id="product_categ_laundry_root" model="product.category">
<field name="name">Laundry Services</field>
</record>
<record id="product_categ_washing" model="product.category">
<field name="name">Washing</field>
<field name="parent_id" ref="product_categ_laundry_root"/>
</record>
<record id="product_categ_dry_cleaning" model="product.category">
<field name="name">Dry Cleaning</field>
<field name="parent_id" ref="product_categ_laundry_root"/>
</record>
<record id="product_categ_ironing" model="product.category">
<field name="name">Ironing &amp; Pressing</field>
<field name="parent_id" ref="product_categ_laundry_root"/>
</record>
<record id="product_categ_special_care" model="product.category">
<field name="name">Special Care</field>
<field name="parent_id" ref="product_categ_laundry_root"/>
</record>
<record id="product_categ_express" model="product.category">
<field name="name">Express Service</field>
<field name="parent_id" ref="product_categ_laundry_root"/>
</record>
<!-- ═══════════════════════════════════════════════════════════════
LAUNDRY SERVICE PRODUCTS — WASHING
Using product.product so XML IDs are directly referenceable.
Template fields (name, categ_id, type, lst_price) are set via
product.product's delegation inheritance to product.template.
═══════════════════════════════════════════════════════════════ -->
<record id="svc_wash_shirt" model="product.product">
<field name="name">Shirt — Wash &amp; Iron</field>
<field name="type">service</field>
<field name="lst_price">5.00</field>
<field name="categ_id" ref="product_categ_washing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="description_sale">Standard wash and press for dress shirts and casual shirts.</field>
</record>
<record id="svc_wash_trousers" model="product.product">
<field name="name">Trousers — Wash &amp; Iron</field>
<field name="type">service</field>
<field name="lst_price">6.00</field>
<field name="categ_id" ref="product_categ_washing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_wash_tshirt" model="product.product">
<field name="name">T-Shirt / Polo — Wash</field>
<field name="type">service</field>
<field name="lst_price">4.00</field>
<field name="categ_id" ref="product_categ_washing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_wash_jeans" model="product.product">
<field name="name">Jeans — Wash</field>
<field name="type">service</field>
<field name="lst_price">7.00</field>
<field name="categ_id" ref="product_categ_washing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_wash_abaya" model="product.product">
<field name="name">Abaya — Wash &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">10.00</field>
<field name="categ_id" ref="product_categ_washing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_wash_thobe" model="product.product">
<field name="name">Thobe / Dishdasha — Wash &amp; Iron</field>
<field name="type">service</field>
<field name="lst_price">9.00</field>
<field name="categ_id" ref="product_categ_washing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_wash_blanket" model="product.product">
<field name="name">Blanket / Duvet — Wash</field>
<field name="type">service</field>
<field name="lst_price">18.00</field>
<field name="categ_id" ref="product_categ_washing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<!-- ═══════════════════════════════════════════════════════════════
LAUNDRY SERVICE PRODUCTS — DRY CLEANING
═══════════════════════════════════════════════════════════════ -->
<record id="svc_dc_suit" model="product.product">
<field name="name">Suit (2-Piece) — Dry Clean</field>
<field name="type">service</field>
<field name="lst_price">25.00</field>
<field name="categ_id" ref="product_categ_dry_cleaning"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="description_sale">Full dry cleaning for 2-piece suits.</field>
</record>
<record id="svc_dc_jacket" model="product.product">
<field name="name">Jacket / Blazer — Dry Clean</field>
<field name="type">service</field>
<field name="lst_price">15.00</field>
<field name="categ_id" ref="product_categ_dry_cleaning"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_dc_dress" model="product.product">
<field name="name">Dress / Gown — Dry Clean</field>
<field name="type">service</field>
<field name="lst_price">20.00</field>
<field name="categ_id" ref="product_categ_dry_cleaning"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_dc_abaya" model="product.product">
<field name="name">Abaya — Dry Clean &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">18.00</field>
<field name="categ_id" ref="product_categ_dry_cleaning"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_dc_thobe" model="product.product">
<field name="name">Thobe / Dishdasha — Dry Clean</field>
<field name="type">service</field>
<field name="lst_price">16.00</field>
<field name="categ_id" ref="product_categ_dry_cleaning"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_dc_wedding_dress" model="product.product">
<field name="name">Wedding Dress — Dry Clean &amp; Preserve</field>
<field name="type">service</field>
<field name="lst_price">60.00</field>
<field name="categ_id" ref="product_categ_dry_cleaning"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="description_sale">Premium dry cleaning and preservation for wedding dresses.</field>
</record>
<!-- ═══════════════════════════════════════════════════════════════
LAUNDRY SERVICE PRODUCTS — IRONING & PRESSING
═══════════════════════════════════════════════════════════════ -->
<record id="svc_iron_shirt" model="product.product">
<field name="name">Shirt — Iron &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">3.00</field>
<field name="categ_id" ref="product_categ_ironing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_iron_trousers" model="product.product">
<field name="name">Trousers — Iron &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">4.00</field>
<field name="categ_id" ref="product_categ_ironing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_iron_thobe" model="product.product">
<field name="name">Thobe / Dishdasha — Iron &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">5.00</field>
<field name="categ_id" ref="product_categ_ironing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_iron_suit" model="product.product">
<field name="name">Suit — Iron &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">8.00</field>
<field name="categ_id" ref="product_categ_ironing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_iron_dress" model="product.product">
<field name="name">Dress / Gown — Iron &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">7.00</field>
<field name="categ_id" ref="product_categ_ironing"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<!-- ═══════════════════════════════════════════════════════════════
LAUNDRY SERVICE PRODUCTS — SPECIAL CARE
═══════════════════════════════════════════════════════════════ -->
<record id="svc_special_carpet" model="product.product">
<field name="name">Carpet / Rug — Deep Clean</field>
<field name="type">service</field>
<field name="lst_price">35.00</field>
<field name="categ_id" ref="product_categ_special_care"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="description_sale">Deep steam cleaning for carpets and rugs.</field>
</record>
<record id="svc_special_curtain" model="product.product">
<field name="name">Curtain — Wash &amp; Press</field>
<field name="type">service</field>
<field name="lst_price">25.00</field>
<field name="categ_id" ref="product_categ_special_care"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_special_leather" model="product.product">
<field name="name">Leather Jacket — Clean &amp; Condition</field>
<field name="type">service</field>
<field name="lst_price">40.00</field>
<field name="categ_id" ref="product_categ_special_care"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
</record>
<record id="svc_special_stain" model="product.product">
<field name="name">Stain Treatment (per item)</field>
<field name="type">service</field>
<field name="lst_price">12.00</field>
<field name="categ_id" ref="product_categ_special_care"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="description_sale">Targeted stain removal treatment applied before washing.</field>
</record>
<!-- ═══════════════════════════════════════════════════════════════
LAUNDRY SERVICE PRODUCTS — EXPRESS SERVICE
═══════════════════════════════════════════════════════════════ -->
<record id="svc_express_4hr" model="product.product">
<field name="name">Express Turnaround (4-Hour)</field>
<field name="type">service</field>
<field name="lst_price">10.00</field>
<field name="categ_id" ref="product_categ_express"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="description_sale">Priority processing — ready within 4 hours.</field>
</record>
<record id="svc_express_sameday" model="product.product">
<field name="name">Same-Day Delivery Surcharge</field>
<field name="type">service</field>
<field name="lst_price">8.00</field>
<field name="categ_id" ref="product_categ_express"/>
<field name="is_laundry_service">True</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="description_sale">Add-on fee for same-day home delivery.</field>
</record>
<!-- ═══════════════════════════════════════════════════════════════
POLISHED EXAMPLE — Service Speed variant.
Demonstrates the cleanest workflow: ONE template, two timing
variants (Normal / Express). Cashier picks variant in POS.
Uses native product.attribute machinery.
═══════════════════════════════════════════════════════════════ -->
<record id="lm_attr_service_speed" model="product.attribute">
<field name="name">Service Speed</field>
<field name="display_type">radio</field>
<field name="create_variant">always</field>
</record>
<record id="lm_attr_speed_normal" model="product.attribute.value">
<field name="name">Normal</field>
<field name="attribute_id" ref="lm_attr_service_speed"/>
<field name="sequence">1</field>
<field name="default_extra_price">0.00</field>
</record>
<record id="lm_attr_speed_express" model="product.attribute.value">
<field name="name">Express</field>
<field name="attribute_id" ref="lm_attr_service_speed"/>
<field name="sequence">2</field>
<field name="default_extra_price">3.00</field>
</record>
<!-- Demo product removed; canonical catalog lives in
data/service_catalog_data.xml (Abaya / Thobe / T-Shirt). -->
</odoo>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Laundry Order sequence: LND/2025/04/0001 -->
<record id="seq_laundry_order" model="ir.sequence">
<field name="name">Laundry Order</field>
<field name="code">laundry.order</field>
<field name="prefix">LND/%(year)s/%(month)s/</field>
<field name="padding">4</field>
<field name="number_increment">1</field>
<field name="number_next">1</field>
<field name="use_date_range" eval="False"/>
<field name="company_id" eval="False"/>
</record>
<!-- Laundry Session sequence (kept for backward compat) -->
<record id="seq_laundry_session" model="ir.sequence">
<field name="name">Laundry Session</field>
<field name="code">laundry.session</field>
<field name="prefix">LS/%(year)s/%(month)s/</field>
<field name="padding">4</field>
<field name="number_increment">1</field>
<field name="number_next">1</field>
<field name="use_date_range" eval="False"/>
<field name="company_id" eval="False"/>
</record>
<!-- Per-item tracking code: LI-000001 (6-digit pad, no date).
Used as the scannable barcode on each laundry.order.line. -->
<record id="seq_laundry_order_line_tracking" model="ir.sequence">
<field name="name">Laundry Item Tracking Code</field>
<field name="code">laundry.order.line.tracking</field>
<field name="prefix">LI-</field>
<field name="padding">6</field>
<field name="number_increment">1</field>
<field name="number_next">1</field>
<field name="use_date_range" eval="False"/>
<field name="company_id" eval="False"/>
</record>
</odoo>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Placeholder to satisfy __manifest__.py.
Add service catalog seed data here later if needed. -->
</odoo>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- Laundry orders themselves are created automatically from POS
(pos_order_id is required), so we don't seed laundry.order rows.
Below: a realistic demo service product with Normal / Express
variants so cashiers see something cleaner than a blank catalog. -->
<!-- ── Product attribute: Service Speed ─────────────────────────── -->
<record id="demo_attr_service_speed" model="product.attribute">
<field name="name">Service Speed</field>
<field name="display_type">radio</field>
<field name="create_variant">always</field>
<field name="sequence">10</field>
</record>
<record id="demo_attr_value_normal" model="product.attribute.value">
<field name="name">Normal</field>
<field name="attribute_id" ref="demo_attr_service_speed"/>
<field name="sequence">10</field>
</record>
<record id="demo_attr_value_express" model="product.attribute.value">
<field name="name">Express</field>
<field name="attribute_id" ref="demo_attr_service_speed"/>
<field name="default_extra_price">3.0</field>
<field name="sequence">20</field>
</record>
<!-- ── Demo product: T-Shirt / Polo Wash ───────────────────────── -->
<record id="demo_product_tshirt_wash" model="product.template">
<field name="name">T-Shirt / Polo Wash</field>
<field name="type">service</field>
<field name="sale_ok">True</field>
<field name="purchase_ok">False</field>
<field name="available_in_pos">True</field>
<field name="is_laundry_service">True</field>
<field name="list_price">8.0</field>
<field name="taxes_id" eval="[(5,)]"/>
<field name="description_sale">Wash + iron for tops. Express adds 3 SAR.</field>
</record>
<record id="demo_product_tshirt_wash_attr_line" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="demo_product_tshirt_wash"/>
<field name="attribute_id" ref="demo_attr_service_speed"/>
<field name="value_ids" eval="[(6, 0, [
ref('demo_attr_value_normal'),
ref('demo_attr_value_express'),
])]"/>
</record>
</odoo>

View File

@@ -0,0 +1,7 @@
## Module <laundry_management>
#### 26.01.2026
#### Version 19.0.1.0.0
#### ADD
- Initial commit for Laundry Management

View File

@@ -0,0 +1,413 @@
# Arabic translation for laundry_management
# Copyright (C) 2026
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-04-01 00:00+0000\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#. module: laundry_management
msgid "Laundry"
msgstr "المغسلة"
#. module: laundry_management
msgid "Laundry Management"
msgstr "إدارة المغسلة"
#. module: laundry_management
msgid "Laundry Order"
msgstr "طلب مغسلة"
#. module: laundry_management
msgid "Laundry Orders"
msgstr "طلبات المغسلة"
#. module: laundry_management
msgid "Orders"
msgstr "الطلبات"
#. module: laundry_management
msgid "All Orders"
msgstr "جميع الطلبات"
#. module: laundry_management
msgid "Operations Pipeline"
msgstr "خط سير العمليات"
#. module: laundry_management
msgid "Ready for Pickup"
msgstr "جاهز للاستلام"
#. module: laundry_management
msgid "Configuration"
msgstr "الإعدادات"
#. module: laundry_management
msgid "Service Categories"
msgstr "تصنيفات الخدمات"
#. module: laundry_management
msgid "Item Types"
msgstr "أنواع القطع"
#. module: laundry_management
msgid "Order No."
msgstr "رقم الطلب"
#. module: laundry_management
msgid "Customer"
msgstr "العميل"
#. module: laundry_management
msgid "Mobile"
msgstr "الجوال"
#. module: laundry_management
msgid "Order Date"
msgstr "تاريخ الطلب"
#. module: laundry_management
msgid "Expected Delivery"
msgstr "موعد التسليم المتوقع"
#. module: laundry_management
msgid "Order Type"
msgstr "نوع الطلب"
#. module: laundry_management
msgid "Walk-In / Drop-Off"
msgstr "مراجعة مباشرة"
#. module: laundry_management
msgid "We Pick Up"
msgstr "نستلم من عندك"
#. module: laundry_management
msgid "Home Delivery"
msgstr "توصيل للمنزل"
#. module: laundry_management
msgid "Priority"
msgstr "الأولوية"
#. module: laundry_management
msgid "Normal"
msgstr "عادي"
#. module: laundry_management
msgid "Urgent"
msgstr "عاجل"
#. module: laundry_management
msgid "Express"
msgstr "سريع جداً"
#. module: laundry_management
msgid "Status"
msgstr "الحالة"
#. module: laundry_management
msgid "Draft"
msgstr "مسودة"
#. module: laundry_management
msgid "Received"
msgstr "مستلم"
#. module: laundry_management
msgid "In Process"
msgstr "تحت المعالجة"
#. module: laundry_management
msgid "Ready"
msgstr "جاهز"
#. module: laundry_management
msgid "Handed Over"
msgstr "تم التسليم"
#. module: laundry_management
msgid "Cancelled"
msgstr "ملغي"
#. module: laundry_management
msgid "Confirm Receipt"
msgstr "تأكيد الاستلام"
#. module: laundry_management
msgid "Start Processing"
msgstr "بدء المعالجة"
#. module: laundry_management
msgid "Ready for Pickup"
msgstr "جاهز للاستلام"
#. module: laundry_management
msgid "Hand Over to Customer"
msgstr "تسليم للعميل"
#. module: laundry_management
msgid "Cancel Order"
msgstr "إلغاء الطلب"
#. module: laundry_management
msgid "Reopen"
msgstr "إعادة فتح"
#. module: laundry_management
msgid "Items & Services"
msgstr "القطع والخدمات"
#. module: laundry_management
msgid "Item"
msgstr "القطعة"
#. module: laundry_management
msgid "Item Type"
msgstr "نوع القطعة"
#. module: laundry_management
msgid "Service"
msgstr "الخدمة"
#. module: laundry_management
msgid "Category"
msgstr "التصنيف"
#. module: laundry_management
msgid "Color / Description"
msgstr "اللون / الوصف"
#. module: laundry_management
msgid "Stain / Damage"
msgstr "بقعة / ضرر"
#. module: laundry_management
msgid "Special Instructions"
msgstr "تعليمات خاصة"
#. module: laundry_management
msgid "Qty"
msgstr "الكمية"
#. module: laundry_management
msgid "Unit Price"
msgstr "سعر الوحدة"
#. module: laundry_management
msgid "Subtotal"
msgstr "المجموع الجزئي"
#. module: laundry_management
msgid "Total"
msgstr "الإجمالي"
#. module: laundry_management
msgid "Total Amount"
msgstr "المبلغ الإجمالي"
#. module: laundry_management
msgid "Items"
msgstr "القطع"
#. module: laundry_management
msgid "Payment"
msgstr "الدفع"
#. module: laundry_management
msgid "Unpaid"
msgstr "غير مدفوع"
#. module: laundry_management
msgid "Partial"
msgstr "مدفوع جزئياً"
#. module: laundry_management
msgid "Paid"
msgstr "مدفوع"
#. module: laundry_management
msgid "Invoice"
msgstr "الفاتورة"
#. module: laundry_management
msgid "Received By"
msgstr "استُلم بواسطة"
#. module: laundry_management
msgid "Handed Over By"
msgstr "سُلّم بواسطة"
#. module: laundry_management
msgid "Customer Notes"
msgstr "ملاحظات العميل"
#. module: laundry_management
msgid "Internal Notes"
msgstr "ملاحظات داخلية"
#. module: laundry_management
msgid "Processing Info"
msgstr "معلومات المعالجة"
#. module: laundry_management
msgid "Notes"
msgstr "الملاحظات"
#. module: laundry_management
msgid "Washing"
msgstr "غسيل"
#. module: laundry_management
msgid "Dry Cleaning"
msgstr "تنظيف جاف"
#. module: laundry_management
msgid "Ironing & Pressing"
msgstr "كوي وضغط"
#. module: laundry_management
msgid "Special Care"
msgstr "عناية خاصة"
#. module: laundry_management
msgid "Express Service"
msgstr "خدمة سريعة"
#. module: laundry_management
msgid "Shirt"
msgstr "قميص"
#. module: laundry_management
msgid "Trousers / Pants"
msgstr "بنطلون"
#. module: laundry_management
msgid "Suit (2-Piece)"
msgstr "بدلة (قطعتان)"
#. module: laundry_management
msgid "Abaya"
msgstr "عباية"
#. module: laundry_management
msgid "Thobe / Dishdasha"
msgstr "ثوب / دشداشة"
#. module: laundry_management
msgid "Dress / Gown"
msgstr "فستان / ثوب سهرة"
#. module: laundry_management
msgid "Jacket / Blazer"
msgstr "جاكيت / بليزر"
#. module: laundry_management
msgid "T-Shirt / Polo"
msgstr "تيشيرت / بولو"
#. module: laundry_management
msgid "Jeans"
msgstr "جينز"
#. module: laundry_management
msgid "Blanket / Duvet"
msgstr "بطانية / لحاف"
#. module: laundry_management
msgid "Carpet / Rug"
msgstr "سجادة / موكيت"
#. module: laundry_management
msgid "Curtain"
msgstr "ستارة"
#. module: laundry_management
msgid "Uniform / Workwear"
msgstr "يونيفورم / ملابس عمل"
#. module: laundry_management
msgid "Wedding Dress"
msgstr "فستان زفاف"
#. module: laundry_management
msgid "Other / Custom Item"
msgstr "أخرى / قطعة مخصصة"
#. module: laundry_management
msgid "Laundry Order Receipt"
msgstr "إيصال طلب المغسلة"
#. module: laundry_management
msgid "Search Orders"
msgstr "البحث في الطلبات"
#. module: laundry_management
msgid "Today"
msgstr "اليوم"
#. module: laundry_management
msgid "Pending"
msgstr "قيد التنفيذ"
#. module: laundry_management
msgid "Delivered"
msgstr "مسلّم"
#. module: laundry_management
msgid "Laundry Service Category"
msgstr "تصنيف خدمة المغسلة"
#. module: laundry_management
msgid "Laundry Item Type"
msgstr "نوع قطعة المغسلة"
#. module: laundry_management
msgid "Laundry Order Line"
msgstr "سطر طلب المغسلة"
#. module: laundry_management
msgid "Default Service"
msgstr "الخدمة الافتراضية"
#. module: laundry_management
msgid "Default Unit Price"
msgstr "السعر الافتراضي للوحدة"
#. module: laundry_management
msgid "Active"
msgstr "نشط"
# ── Reporting menus integrated into Sales/Accounting dashboards ──────
#. module: laundry_management
msgid "Laundry Orders Analysis"
msgstr "تحليل طلبات المغسلة"
#. module: laundry_management
msgid "Laundry Operations (Live)"
msgstr "عمليات المغسلة (مباشر)"
#. module: laundry_management
msgid "Laundry Invoices"
msgstr "فواتير المغسلة"
#. module: laundry_management
msgid "Reports"
msgstr "التقارير"
#. module: laundry_management
msgid "Orders Analysis"
msgstr "تحليل الطلبات"
#. module: laundry_management
msgid "Operations (Live)"
msgstr "العمليات (مباشر)"
#. module: laundry_management
msgid "Invoices Analysis"
msgstr "تحليل الفواتير"

View File

@@ -0,0 +1,228 @@
"""
Pre-migration script for laundry_management 19.0.11.0.0
Architecture change: standalone laundry.order / laundry.order.line / laundry.payment
models are replaced by _inherit = 'sale.order' / 'sale.order.line'.
This script runs BEFORE Odoo's model sync so that:
1. FK constraints from old wizard tables referencing laundry_order are dropped
2. Old wizard transient records are purged (they reference non-existent rows)
3. Stale ir.model.fields records pointing to old models are removed
4. Old ir.model entries are deleted (unblocking the ORM delete check)
5. Old physical tables are dropped
Without this, the ORM raises:
"Another model is using the record you are trying to delete.
Blocking model: Laundry Print Wizard (laundry.print.wizard),
Blocking field: order_id"
"""
import logging
_logger = logging.getLogger(__name__)
# Old standalone models being removed in this version
_OLD_MODELS = [
'laundry.order',
'laundry.order.line',
'laundry.payment',
'laundry.order.line.addon',
'laundry.register.payment.wizard',
'laundry.product.wizard',
'laundry.product.wizard.line',
'laundry.category',
'laundry.item.type',
]
# Physical tables that correspond to the old models
_OLD_TABLES = [
'laundry_order',
'laundry_order_line',
'laundry_payment',
'laundry_order_line_addon',
'laundry_register_payment_wizard',
'laundry_product_wizard',
'laundry_product_wizard_line',
'laundry_category',
'laundry_item_type',
]
# Transient/wizard tables that may hold rows with FK refs to laundry_order
_WIZARD_TABLES = [
'laundry_print_wizard',
'laundry_session_wizard',
'laundry_whatsapp_wizard',
'laundry_register_payment_wizard',
'laundry_product_wizard',
'laundry_product_wizard_line',
]
def _table_exists(cr, table):
cr.execute(
"SELECT 1 FROM information_schema.tables "
"WHERE table_schema = 'public' AND table_name = %s",
(table,),
)
return bool(cr.fetchone())
def _column_exists(cr, table, column):
cr.execute(
"SELECT 1 FROM information_schema.columns "
"WHERE table_schema = 'public' AND table_name = %s AND column_name = %s",
(table, column),
)
return bool(cr.fetchone())
def _drop_fk_constraints_referencing(cr, referenced_table):
"""Drop all FK constraints in the DB that point at referenced_table."""
cr.execute(
"""
SELECT tc.table_name, tc.constraint_name
FROM information_schema.table_constraints tc
JOIN information_schema.referential_constraints rc
ON tc.constraint_name = rc.constraint_name
AND tc.table_schema = rc.constraint_schema
JOIN information_schema.table_constraints tc2
ON rc.unique_constraint_name = tc2.constraint_name
AND rc.unique_constraint_schema = tc2.table_schema
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc2.table_name = %s
AND tc.table_schema = 'public'
""",
(referenced_table,),
)
rows = cr.fetchall()
for src_table, constraint_name in rows:
_logger.info(
'pre_migrate: dropping FK constraint %s on %s (referenced %s)',
constraint_name, src_table, referenced_table,
)
cr.execute(
'ALTER TABLE "%s" DROP CONSTRAINT IF EXISTS "%s"' %
(src_table, constraint_name)
)
def migrate(cr, version):
if not version:
# Fresh install — nothing to clean up
return
_logger.info('pre_migrate laundry_management %s → 19.0.11.0.0 : start', version)
# ── Step 1: Purge all transient wizard records ────────────────────────────
# TransientModel rows expire naturally but DB rows persist during upgrade;
# they hold FK refs that block constraint drops and table drops.
for tbl in _WIZARD_TABLES:
if _table_exists(cr, tbl):
_logger.info('pre_migrate: truncating wizard table %s', tbl)
cr.execute('TRUNCATE TABLE "%s" CASCADE' % tbl)
# ── Step 2: Drop FK constraints pointing at old tables ────────────────────
for tbl in _OLD_TABLES:
if _table_exists(cr, tbl):
_drop_fk_constraints_referencing(cr, tbl)
# Also drop FKs on wizard tables that point at laundry_order (the primary blocker)
# Example: laundry_print_wizard.order_id -> laundry_order.id
for wizard_tbl in _WIZARD_TABLES:
if not _table_exists(cr, wizard_tbl):
continue
# Find and drop any FK on this wizard table that references an old table
cr.execute(
"""
SELECT kcu.column_name, ccu.table_name AS foreign_table_name, tc.constraint_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
AND ccu.table_schema = tc.table_schema
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_schema = 'public'
AND tc.table_name = %s
AND ccu.table_name = ANY(%s)
""",
(wizard_tbl, _OLD_TABLES),
)
for col, ref_tbl, constraint_name in cr.fetchall():
_logger.info(
'pre_migrate: dropping FK %s on %s.%s -> %s',
constraint_name, wizard_tbl, col, ref_tbl,
)
cr.execute(
'ALTER TABLE "%s" DROP CONSTRAINT IF EXISTS "%s"' %
(wizard_tbl, constraint_name)
)
# ── Step 3: Remove stale ir.model.fields that ref old models ─────────────
# These are the records that cause "Blocking model: laundry.print.wizard,
# Blocking field: order_id" — the field record itself still has
# relation = 'laundry.order', which makes the ORM think laundry.print.wizard
# still depends on laundry.order.
cr.execute(
"""
DELETE FROM ir_model_fields
WHERE relation = ANY(%s)
""",
(_OLD_MODELS,),
)
deleted = cr.rowcount
_logger.info('pre_migrate: deleted %d stale ir.model.fields rows', deleted)
# Also remove fields whose model itself is one of the old models
cr.execute(
"""
DELETE FROM ir_model_fields
WHERE model IN %s
""",
(tuple(_OLD_MODELS),),
)
_logger.info('pre_migrate: deleted %d ir.model.fields for old models', cr.rowcount)
# ── Step 4: Remove stale ir.model.fields_by_name cache ───────────────────
# In some Odoo versions there is a separate constraint/index table.
# Safe to attempt; ignore if table doesn't exist.
if _table_exists(cr, 'ir_model_constraint'):
cr.execute(
"""
DELETE FROM ir_model_constraint imc
USING ir_model im
WHERE imc.model = im.id
AND im.model = ANY(%s)
""",
(_OLD_MODELS,),
)
_logger.info('pre_migrate: removed %d ir.model.constraint rows', cr.rowcount)
if _table_exists(cr, 'ir_model_relation'):
cr.execute(
"""
DELETE FROM ir_model_relation imr
USING ir_model im
WHERE imr.model = im.id
AND im.model = ANY(%s)
""",
(_OLD_MODELS,),
)
_logger.info('pre_migrate: removed %d ir.model.relation rows', cr.rowcount)
# ── Step 5: Remove ir.model entries for the old models ───────────────────
cr.execute(
"DELETE FROM ir_model WHERE model = ANY(%s)",
(_OLD_MODELS,),
)
_logger.info('pre_migrate: deleted %d ir.model rows', cr.rowcount)
# ── Step 6: Drop old physical tables ─────────────────────────────────────
for tbl in reversed(_OLD_TABLES): # reverse to respect FK order
if _table_exists(cr, tbl):
_logger.info('pre_migrate: dropping table %s', tbl)
cr.execute('DROP TABLE IF EXISTS "%s" CASCADE' % tbl)
_logger.info('pre_migrate laundry_management: complete')

View File

@@ -0,0 +1,48 @@
"""
Pre-migration for laundry_management 19.0.12.0.0
Changes handled:
1. Commission states: 'paid' only → now 'pending', 'confirmed', 'paid'
Existing 'paid' rows remain 'paid'. Existing 'pending' rows remain 'pending'.
'confirmed' is a new state — no existing rows use it, so no data migration needed.
2. New model: laundry.payment.wizard — just a new table, nothing to clean.
3. New groups: group_laundry_operator, group_laundry_cashier added to hierarchy.
Existing users with group_laundry_user keep all their permissions (implied).
4. Access control: new CSV entries for payment wizard and new groups.
Handled automatically by Odoo on upgrade.
No destructive operations needed in this migration.
The pre_migrate script from 19.0.11.0.0 already cleaned the legacy tables.
"""
import logging
_logger = logging.getLogger(__name__)
def migrate(cr, version):
if not version:
return # Fresh install
_logger.info('pre_migrate laundry_management 19.0.12.0.0: checking commission states')
# Ensure commission state column allows new 'confirmed' value.
# In Odoo, Selection fields are stored as VARCHAR — no schema change needed.
# Just verify the column exists and log the existing state distribution.
cr.execute("""
SELECT state, COUNT(*) FROM laundry_commission
GROUP BY state
ORDER BY state
""")
rows = cr.fetchall()
if rows:
_logger.info(
'pre_migrate: commission state distribution: %s',
{state: count for state, count in rows}
)
else:
_logger.info('pre_migrate: laundry_commission table is empty — fresh data')
_logger.info('pre_migrate laundry_management 19.0.12.0.0: complete')

View File

@@ -0,0 +1,103 @@
"""Pre-migration: 19.0.12.0.0 → 19.0.13.0.0
Removes three models that are being retired in this version:
- laundry.product.wizard (replaced by native sale.order.line product selection)
- laundry.product.wizard.line (child of above)
- laundry.whatsapp.wizard (replaced by one-click wa.me URL action)
If these ir.model records are left in the DB while the Python classes no longer
exist, Odoo will log warnings or fail on field-level checks during upgrade.
We clean them here, before the ORM loads.
"""
import logging
_logger = logging.getLogger(__name__)
_REMOVED_MODELS = [
'laundry.product.wizard',
'laundry.product.wizard.line',
'laundry.whatsapp.wizard',
]
_REMOVED_TABLES = [
'laundry_product_wizard',
'laundry_product_wizard_line',
'laundry_whatsapp_wizard',
]
def migrate(cr, version):
if not version:
return
_logger.info('pre_migrate 19.0.13.0.0: cleaning retired wizard models %s', _REMOVED_MODELS)
# 1. Drop physical tables (TransientModels do have real tables in Odoo)
for tbl in _REMOVED_TABLES:
cr.execute(f'DROP TABLE IF EXISTS "{tbl}" CASCADE')
_logger.info(' dropped table: %s', tbl)
# 2. Remove ir.model.access entries for these models
cr.execute("""
DELETE FROM ir_model_access
WHERE model_id IN (
SELECT id FROM ir_model WHERE model = ANY(%s)
)
""", (_REMOVED_MODELS,))
_logger.info(' deleted %d ir.model.access rows', cr.rowcount)
# 3. Remove ir.rule entries
cr.execute("""
DELETE FROM ir_rule
WHERE model_id IN (
SELECT id FROM ir_model WHERE model = ANY(%s)
)
""", (_REMOVED_MODELS,))
# 4. Remove ir.model.fields for these models
cr.execute("""
DELETE FROM ir_model_fields
WHERE model_id IN (
SELECT id FROM ir_model WHERE model = ANY(%s)
)
""", (_REMOVED_MODELS,))
_logger.info(' deleted ir.model.fields rows')
# 5. Remove ir.model.constraint
cr.execute("""
DELETE FROM ir_model_constraint
WHERE model_id IN (
SELECT id FROM ir_model WHERE model = ANY(%s)
)
""", (_REMOVED_MODELS,))
# 6. Remove ir.model.relation
cr.execute("""
DELETE FROM ir_model_relation
WHERE model_id IN (
SELECT id FROM ir_model WHERE model = ANY(%s)
)
""", (_REMOVED_MODELS,))
# 7. Remove ir.model.data (XML IDs) for these models
cr.execute("""
DELETE FROM ir_model_data
WHERE model = 'ir.model' AND name IN (
SELECT REPLACE(model, '.', '_') || '_' || id::text
FROM ir_model WHERE model = ANY(%s)
)
""", (_REMOVED_MODELS,))
# Also delete by res_id
cr.execute("""
DELETE FROM ir_model_data
WHERE model = 'ir.model'
AND res_id IN (SELECT id FROM ir_model WHERE model = ANY(%s))
""", (_REMOVED_MODELS,))
# 8. Finally remove ir.model entries themselves
cr.execute("""
DELETE FROM ir_model WHERE model = ANY(%s)
""", (_REMOVED_MODELS,))
_logger.info(' deleted ir.model entries for retired wizards')
_logger.info('pre_migrate 19.0.13.0.0: complete')

View File

@@ -0,0 +1,75 @@
"""Phase 1 financial model migration.
Before: laundry_order.amount_paid was blindly copied from pos_order.amount_paid,
which includes Customer Account / pay-later payments. Every deferred sale
appeared fully paid → amount_due was always 0 → Settle Dues was non-functional.
After: amount_paid_cash + amount_deferred are computed from pos_payment rows,
classified by pos_payment_method.split_transactions. amount_due is recomputed
as amount_deferred - amount_settled.
This script rebuilds the split for every existing laundry.order by replaying
its linked pos_order's payment history.
"""
import logging
_logger = logging.getLogger(__name__)
def migrate(cr, version):
if not version:
return
_logger.info('laundry_management: rebuilding financial split for existing orders')
# Ensure the new columns exist (ORM will have added them, but be defensive)
cr.execute("""
ALTER TABLE laundry_order
ADD COLUMN IF NOT EXISTS amount_paid_cash numeric DEFAULT 0,
ADD COLUMN IF NOT EXISTS amount_deferred numeric DEFAULT 0;
""")
# Rebuild amount_paid_cash / amount_deferred per laundry order
# from pos_payment rows classified by pos_payment_method.split_transactions.
cr.execute("""
WITH classified AS (
SELECT
lo.id AS lo_id,
COALESCE(SUM(CASE WHEN pm.split_transactions = FALSE THEN pp.amount ELSE 0 END), 0) AS cash,
COALESCE(SUM(CASE WHEN pm.split_transactions = TRUE THEN pp.amount ELSE 0 END), 0) AS deferred
FROM laundry_order lo
LEFT JOIN pos_payment pp ON pp.pos_order_id = lo.pos_order_id
LEFT JOIN pos_payment_method pm ON pm.id = pp.payment_method_id
GROUP BY lo.id
)
UPDATE laundry_order lo
SET amount_paid_cash = c.cash,
amount_deferred = c.deferred,
amount_settled = COALESCE(lo.amount_settled, 0)
FROM classified c
WHERE c.lo_id = lo.id;
""")
# amount_due is a stored compute (amount_deferred - amount_settled).
# Populate it directly here so the values are correct before the ORM
# recomputes (ORM recompute on install will overwrite with same result).
cr.execute("""
UPDATE laundry_order
SET amount_due = GREATEST(
COALESCE(amount_deferred, 0) - COALESCE(amount_settled, 0),
0
);
""")
cr.execute("""
SELECT COUNT(*),
SUM(amount_paid_cash),
SUM(amount_deferred),
SUM(amount_due)
FROM laundry_order
""")
row = cr.fetchone()
_logger.info(
'laundry_management migration: %s orders — cash=%.2f, deferred=%.2f, due=%.2f',
row[0] or 0, row[1] or 0.0, row[2] or 0.0, row[3] or 0.0,
)

View File

@@ -0,0 +1,16 @@
from . import product_template_ext # adds is_laundry_service to product.template
from . import laundry_order_type # standalone: laundry.order.type
from . import laundry_order_attribute # standalone: laundry.order.attribute
from . import laundry_order # standalone: laundry.order
from . import laundry_order_line # standalone: laundry.order.line
from . import pos_order # extends pos.order with sync_from_ui hook
from . import pos_config_ext # extends pos.config with laundry-pos settings
from . import res_partner # extends res.partner with laundry unpaid count
from . import laundry_order_line_addon # add-on services per order line
from . import laundry_commission # standalone: commission tracking
# NOTE: laundry_dashboard removed — depends on laundry.session (POS-owned)
from . import laundry_payment_method # standalone: configurable payment methods
from . import laundry_settings # extends res.config.settings
from . import account_payment_ext # stamps pos_session_id on settlement payments
from . import pos_session_ext # ships new POS models to the client
# NOTE: laundry_session and account_move removed — session/accounting is POS-owned

View File

@@ -0,0 +1,21 @@
from odoo import models, fields, api
class AccountMoveLaundryExt(models.Model):
"""Flag invoices originating from laundry orders."""
_inherit = 'account.move'
is_laundry_invoice = fields.Boolean(
string='Laundry Invoice',
compute='_compute_is_laundry_invoice',
store=True,
)
@api.depends('invoice_line_ids.sale_line_ids.order_id.is_laundry_order')
def _compute_is_laundry_invoice(self):
for move in self:
move.is_laundry_invoice = any(
sol.order_id.is_laundry_order
for line in move.invoice_line_ids
for sol in line.sale_line_ids
)

View File

@@ -0,0 +1,22 @@
from odoo import models, fields
class AccountPaymentLaundryExt(models.Model):
"""Informational stamp — links settlement payments to the POS session
that was open when the cashier collected the money.
This field is purely for visibility (closing-screen summary). It does
NOT inject settlement totals into POS cash-control math.
"""
_inherit = 'account.payment'
pos_session_id = fields.Many2one(
'pos.session', string='POS Session',
readonly=True, copy=False, index=True,
help='POS session that was open when this settlement was created.',
)
settlement_pos_pm_id = fields.Many2one(
'pos.payment.method', string='Settlement Payment Method',
readonly=True, copy=False, index=True,
help='Original POS payment method chosen by the cashier during settlement.',
)

View File

@@ -0,0 +1,125 @@
from odoo import models, fields, api
from odoo.exceptions import UserError
class LaundryCommission(models.Model):
"""Staff commission tracking (PART 3).
States:
pending — auto-created when order progresses; awaiting manager review
confirmed — manager has verified and approved the commission
paid — commission has been settled/paid to the staff member
The commission_account_id (from settings) is informational for now.
Managers can bulk-confirm and bulk-mark-paid from the list view.
"""
_name = 'laundry.commission'
_description = 'Laundry Staff Commission'
_inherit = ['mail.thread']
_order = 'date desc, id desc'
name = fields.Char(
string='Reference',
compute='_compute_name', store=True, readonly=True,
)
order_id = fields.Many2one(
'sale.order', string='Order',
required=True, ondelete='cascade', index=True,
)
company_id = fields.Many2one(
'res.company',
related='order_id.company_id', store=True, index=True,
)
employee_id = fields.Many2one(
'res.users', string='Staff Member',
required=True, domain=[('share', '=', False)],
tracking=True,
)
role = fields.Selection([
('reception', 'Reception / Intake'),
('processing', 'Processing / Cleaning'),
('delivery', 'Delivery / Handover'),
], string='Role', required=True, tracking=True)
commission_type = fields.Selection([
('percentage', 'Percentage (%)'),
('fixed', 'Fixed Amount'),
], string='Type', required=True, default='percentage')
rate = fields.Float(string='Rate / Amount', digits=(10, 2))
base_amount = fields.Float(string='Order Total', digits=(10, 2))
commission_amount = fields.Float(
string='Commission',
compute='_compute_commission_amount', store=True, digits=(10, 2),
)
date = fields.Date(string='Date', required=True, default=fields.Date.today)
state = fields.Selection([
('pending', 'Pending'),
('confirmed', 'Confirmed'),
('paid', 'Paid'),
], default='pending', required=True, tracking=True, copy=False, index=True)
notes = fields.Text(string='Notes')
_ROLE_LABELS = {
'reception': 'Reception',
'processing': 'Processing',
'delivery': 'Delivery',
}
@api.depends('order_id', 'order_id.name', 'role')
def _compute_name(self):
for rec in self:
order = rec.order_id.name or 'NEW'
role = self._ROLE_LABELS.get(rec.role, rec.role or '')
rec.name = f'COM/{order}/{role}'
@api.depends('commission_type', 'rate', 'base_amount')
def _compute_commission_amount(self):
for rec in self:
if rec.commission_type == 'percentage':
rec.commission_amount = rec.base_amount * rec.rate / 100.0
else:
rec.commission_amount = rec.rate
# ── State transitions ─────────────────────────────────────────────
def action_confirm(self):
"""Manager confirms commission is valid and approved."""
for rec in self:
if rec.state != 'pending':
raise UserError(
f'"{rec.name}" cannot be confirmed — current state: {rec.state}.'
)
rec.write({'state': 'confirmed'})
rec.message_post(
body=f'Commission confirmed by {self.env.user.name}. '
f'Amount: {rec.commission_amount:.2f}'
)
def action_mark_paid(self):
"""Mark commission as settled/paid to staff."""
for rec in self:
if rec.state == 'paid':
raise UserError(f'"{rec.name}" is already paid.')
if rec.state == 'pending':
# Allow paying directly from pending (manager shortcut)
rec.write({'state': 'paid'})
else:
rec.write({'state': 'paid'})
rec.message_post(
body=f'Marked as paid by {self.env.user.name}.'
)
def action_reset_pending(self):
"""Reset commission back to pending (manager only)."""
for rec in self:
if rec.state == 'paid':
raise UserError(
f'Cannot reset "{rec.name}" — it has already been paid.'
)
rec.write({'state': 'pending'})
rec.message_post(
body=f'Reset to pending by {self.env.user.name}.'
)

View File

@@ -0,0 +1,180 @@
from odoo import models, fields, api
class LaundryDashboard(models.TransientModel):
"""Live KPI dashboard — queries sale.order with is_laundry_order = True."""
_name = 'laundry.dashboard'
_description = 'Laundry Dashboard'
today_orders = fields.Integer(string="Today's Orders")
today_revenue = fields.Monetary(string="Today's Revenue", currency_field='currency_id')
today_collected = fields.Monetary(string='Collected Today', currency_field='currency_id')
today_outstanding = fields.Monetary(string='Outstanding Today', currency_field='currency_id')
pending_count = fields.Integer(string='Pending Orders')
ready_count = fields.Integer(string='Ready for Pickup')
in_progress_count = fields.Integer(string='In Processing')
draft_count = fields.Integer(string='Quotes / Draft')
session_is_open = fields.Boolean(string='Session Open')
session_name = fields.Char(string='Session')
session_opening_cash = fields.Monetary(string='Opening Float', currency_field='currency_id')
session_sales = fields.Monetary(string='Session Sales', currency_field='currency_id')
session_cash = fields.Monetary(string='Session Cash', currency_field='currency_id')
session_bank = fields.Monetary(string='Session Bank', currency_field='currency_id')
session_id = fields.Many2one('laundry.session', string='Session Link')
month_orders = fields.Integer(string='Orders This Month')
month_revenue = fields.Monetary(string='Revenue This Month', currency_field='currency_id')
month_paid = fields.Monetary(string='Collected This Month', currency_field='currency_id')
currency_id = fields.Many2one(
'res.currency',
default=lambda self: self.env.company.currency_id,
)
@api.model
def _build(self):
today = fields.Date.today()
month_start = today.replace(day=1)
company = self.env.company
Order = self.env['sale.order']
Payment = self.env['account.payment']
_base_domain = [
('is_laundry_order', '=', True),
('company_id', '=', company.id),
]
# ── Today ──────────────────────────────────────────────────────
today_orders = Order.search(_base_domain + [
('date_order', '>=', fields.Datetime.to_datetime(today)),
('state', 'not in', ['cancel', 'draft']),
])
today_invoices = today_orders.mapped('invoice_ids').filtered(
lambda i: i.state == 'posted' and i.move_type == 'out_invoice'
)
today_revenue = sum(today_orders.mapped('amount_total'))
today_outstanding = sum(
max(i.amount_residual, 0.0) for i in today_invoices
)
today_collected = today_revenue - today_outstanding
# ── Pipeline (all active laundry orders) ──────────────────────
pipeline = Order.search(_base_domain + [
('state', '=', 'sale'),
])
pending_count = len(pipeline)
ready_count = len(pipeline.filtered(lambda o: o.laundry_state == 'ready'))
in_progress_count = len(pipeline.filtered(lambda o: o.laundry_state == 'processing'))
draft_count = len(Order.search(_base_domain + [('state', '=', 'draft')]))
# ── Session ────────────────────────────────────────────────────
session = self.env['laundry.session'].search([
('state', '=', 'opened'),
('company_id', '=', company.id),
], limit=1)
# ── Month ──────────────────────────────────────────────────────
month_orders = Order.search(_base_domain + [
('date_order', '>=', fields.Datetime.to_datetime(month_start)),
('state', 'not in', ['cancel', 'draft']),
])
month_invoices = month_orders.mapped('invoice_ids').filtered(
lambda i: i.state == 'posted' and i.move_type == 'out_invoice'
)
month_revenue = sum(month_orders.mapped('amount_total'))
month_outstanding = sum(max(i.amount_residual, 0.0) for i in month_invoices)
month_paid = month_revenue - month_outstanding
return self.create({
'today_orders' : len(today_orders),
'today_revenue' : today_revenue,
'today_collected' : max(today_collected, 0.0),
'today_outstanding' : today_outstanding,
'pending_count' : pending_count,
'ready_count' : ready_count,
'in_progress_count' : in_progress_count,
'draft_count' : draft_count,
'session_is_open' : bool(session),
'session_name' : session.name if session else '',
'session_opening_cash' : session.opening_cash if session else 0.0,
'session_sales' : session.total_sales if session else 0.0,
'session_cash' : session.total_cash if session else 0.0,
'session_bank' : session.total_bank if session else 0.0,
'session_id' : session.id if session else False,
'month_orders' : len(month_orders),
'month_revenue' : month_revenue,
'month_paid' : max(month_paid, 0.0),
})
@api.model
def action_open_dashboard(self):
rec = self._build()
return {
'type': 'ir.actions.act_window',
'name': 'Dashboard',
'res_model': 'laundry.dashboard',
'res_id': rec.id,
'view_mode': 'form',
'target': 'main',
'flags': {'mode': 'readonly'},
}
def action_refresh(self):
return self.action_open_dashboard()
def action_new_order(self):
return {
'type': 'ir.actions.act_window',
'name': 'New Laundry Order',
'res_model': 'sale.order',
'view_mode': 'form',
'target': 'current',
'context': {
'default_is_laundry_order': True,
},
}
def action_open_session(self):
if self.session_id:
return {
'type': 'ir.actions.act_window',
'name': 'Session',
'res_model': 'laundry.session',
'res_id': self.session_id.id,
'view_mode': 'form',
}
return {
'type': 'ir.actions.act_window',
'name': 'Sessions',
'res_model': 'laundry.session',
'view_mode': 'list,form',
}
def action_new_session(self):
return {
'type': 'ir.actions.act_window',
'name': 'New Session',
'res_model': 'laundry.session',
'view_mode': 'form',
}
def action_view_ready_orders(self):
return {
'type': 'ir.actions.act_window',
'name': 'Ready for Pickup',
'res_model': 'sale.order',
'view_mode': 'list,form',
'domain': [('is_laundry_order', '=', True), ('laundry_state', '=', 'ready')],
}
def action_view_pending_orders(self):
return {
'type': 'ir.actions.act_window',
'name': 'Pending Orders',
'res_model': 'sale.order',
'view_mode': 'list,form',
'domain': [('is_laundry_order', '=', True), ('state', '=', 'sale'),
('laundry_state', 'not in', ['delivered'])],
}

View File

@@ -0,0 +1,765 @@
import logging
from odoo import models, fields, api, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
STATES = [
('intake', 'Intake'),
('processing', 'Processing'),
('ready', 'Ready for Pickup'),
('delivered', 'Delivered'),
('cancelled', 'Cancelled'),
]
STATE_KEYS = [s[0] for s in STATES]
FINAL_STATES = {'delivered', 'cancelled'}
SOURCE_TYPES = [
('pos', 'Point of Sale'),
('manual', 'Manual / Backoffice'),
]
# Header fields the lock protects. NOT included on purpose:
# - state (workflow advance is always allowed via dedicated actions)
# - amount_settled (settlement engine writes after lock)
# - notes (managerial commentary always allowed)
# - manager_unlocked_* (the unlock wizard writes these)
# - tracking_enabled (Phase-4 prep, manager configuration)
LOCKED_HEADER_FIELDS = frozenset({
'partner_id',
'amount_total', 'amount_paid_cash', 'amount_deferred',
'order_type_id', 'attribute_ids',
'is_delivery', 'delivery_address', 'delivery_scheduled_at',
'priority_level',
'pos_order_id', 'pos_reference',
'source_type', 'name', 'company_id',
})
# Sentinel context flag set by the POS sync hook (and any other automated
# server-side path) to allow the create + write that bring a fresh
# laundry.order to life. Without this flag, locked orders refuse mutations.
POS_SYNC_CTX = 'laundry_pos_sync'
class LaundryOrder(models.Model):
"""Standalone laundry order — created from POS.
POS owns payments/sessions/accounting. This model handles operational
workflow only: intake -> processing -> ready -> delivered.
Financial model (Phase 1 fix):
amount_total = mirror of pos.order.amount_total
amount_paid_cash = real money collected at origin (cash/card)
amount_deferred = Customer Account / pay-later amount at origin
amount_settled = money collected later via settlement engine
amount_due = amount_deferred - amount_settled
`amount_due > 0` means the customer still owes real money.
"""
_name = 'laundry.order'
_description = 'Laundry Order'
_inherit = ['mail.thread', 'mail.activity.mixin']
_order = 'create_date desc, id desc'
name = fields.Char(
string='Order No.',
required=True, copy=False, readonly=True,
default='New', tracking=True,
)
# -- POS link --
# Optional: manual/backoffice orders have no POS origin. The uniqueness
# constraint below still enforces "one laundry order per pos.order" for
# POS-sourced rows.
pos_order_id = fields.Many2one(
'pos.order', string='POS Order',
index=True, readonly=True, copy=False,
ondelete='restrict',
)
pos_reference = fields.Char(
string='POS Reference',
readonly=True, copy=False, index=True,
)
# -- Customer --
partner_id = fields.Many2one(
'res.partner', string='Customer',
required=True, index=True, tracking=True,
)
partner_phone = fields.Char(
related='partner_id.phone', string='Phone', readonly=True,
)
# -- Company / Currency --
company_id = fields.Many2one(
'res.company', string='Company',
required=True, default=lambda self: self.env.company,
index=True,
)
currency_id = fields.Many2one(
related='company_id.currency_id', store=True,
)
# -- Workflow --
state = fields.Selection(
STATES,
string='Status',
default='intake',
required=True,
tracking=True,
copy=False,
index=True,
)
# -- Lines --
line_ids = fields.One2many(
'laundry.order.line', 'order_id', string='Order Lines',
)
# -- Financial snapshot --
amount_total = fields.Monetary(
string='Total',
currency_field='currency_id',
readonly=True, copy=False, store=True,
)
amount_paid_cash = fields.Monetary(
string='Paid (Cash/Card)',
currency_field='currency_id',
readonly=True, copy=False, store=True,
default=0.0,
help='Amount collected at origin via non-deferred payment methods '
'(cash, card — any pos.payment.method with split_transactions=False).',
)
amount_deferred = fields.Monetary(
string='Deferred',
currency_field='currency_id',
readonly=True, copy=False, store=True,
default=0.0,
help='Amount deferred at origin via Customer Account / pay-later '
'(any pos.payment.method with split_transactions=True).',
)
amount_settled = fields.Monetary(
string='Settled',
currency_field='currency_id',
readonly=True, copy=False, store=True,
default=0.0,
help='Amount collected later via the settlement engine '
'(account.payment + reconciliation).',
)
amount_due = fields.Monetary(
string='Due',
currency_field='currency_id',
compute='_compute_amount_due',
store=True,
help='Remaining balance the customer owes: amount_deferred - amount_settled.',
)
# Back-compat alias — views/reports still reference `amount_paid`.
# Computed, non-stored; reflects real cash collected at origin.
amount_paid = fields.Monetary(
string='Paid',
currency_field='currency_id',
compute='_compute_amount_paid_alias',
)
# -- Computed --
item_count = fields.Integer(
string='Items',
compute='_compute_item_count',
store=True,
)
# -- Order type / attributes / delivery --
order_type_id = fields.Many2one(
'laundry.order.type', string='Order Type',
index=True, tracking=True,
)
attribute_ids = fields.Many2many(
'laundry.order.attribute',
'laundry_order_attribute_rel',
'order_id', 'attribute_id',
string='Attributes',
)
is_delivery = fields.Boolean(string='Delivery', tracking=True, index=True)
delivery_address = fields.Text(string='Delivery Address')
delivery_scheduled_at = fields.Datetime(string='Scheduled At')
priority_level = fields.Selection([
('normal', 'Normal'),
('urgent', 'Urgent'),
], string='Priority', default='normal', tracking=True, index=True)
# -- Notes --
notes = fields.Text(string='Notes')
# ── Source / locking (Phase 3) ────────────────────────────────────
# source_type is the truth-bearing identity. is_from_pos is a stored
# mirror used in domains, list filters, and rule conditions where a
# selection field would be awkward.
source_type = fields.Selection(
SOURCE_TYPES,
string='Source',
required=True,
default='manual',
readonly=True,
copy=False,
index=True,
tracking=True,
help='POS-sourced orders are hard-locked: lines, prices and the '
'customer cannot be edited unless a manager grants a '
'temporary unlock window. Manual orders are editable until '
'they reach a final state (delivered / cancelled).',
)
is_from_pos = fields.Boolean(
string='From POS',
compute='_compute_is_from_pos',
store=True, index=True,
)
# Phase-4 prep — flag only, no logic wired yet.
tracking_enabled = fields.Boolean(
string='Per-Item Tracking',
default=False,
copy=False,
help='When enabled, each laundry.order.line will be advanced '
'through its own state machine. Phase 4 wires the '
'synchronization between order state and item state.',
)
# Computed: True when the order refuses mutation of LOCKED_HEADER_FIELDS
# and any line write/create/unlink. Not stored — cheap to recompute and
# depends on a Datetime that ages out without a write.
locked = fields.Boolean(
string='Locked',
compute='_compute_locked',
help='Order is read-only when True. POS-sourced orders are '
'always locked. Final-state orders (delivered, cancelled) '
'are always locked. Managers can grant a temporary unlock '
'window via the "Unlock for Editing" action.',
)
manager_unlocked_until = fields.Datetime(
string='Unlock Window Expires',
copy=False, readonly=True,
help='When set in the future, the lock guard is suspended. '
'Auto-expires; no manual re-lock required.',
)
manager_unlocked_by = fields.Many2one(
'res.users', string='Last Unlocked By',
copy=False, readonly=True,
)
manager_unlock_reason = fields.Char(
string='Last Unlock Reason',
copy=False, readonly=True,
)
# Stamped when the order moves to delivered. Powers the avg-processing
# and on-time KPIs on the Operations Dashboard. Outside
# LOCKED_HEADER_FIELDS so action_deliver can write it on POS-locked
# orders without needing the bypass context.
delivered_at = fields.Datetime(
string='Delivered At',
readonly=True, copy=False, index=True,
help='Timestamp set automatically when the order moves to '
'Delivered. Used by the analytics dashboard to compute '
'processing time and on-time delivery percentage.',
)
# -- Constraints --
_pos_order_uniq = models.Constraint(
'UNIQUE(pos_order_id)',
'A laundry order already exists for this POS order.',
)
@api.constrains('source_type', 'pos_order_id')
def _check_source_type_consistency(self):
for order in self:
if order.source_type == 'pos' and not order.pos_order_id:
raise UserError(_(
'Order "%s" is marked as POS-sourced but has no '
'linked POS order.', order.name or order.id,
))
if order.source_type == 'manual' and order.pos_order_id:
raise UserError(_(
'Order "%s" is marked as manual but has a linked '
'POS order. Set source_type="pos" to keep them '
'consistent.', order.name or order.id,
))
# -- Computed --
@api.depends('amount_deferred', 'amount_settled')
def _compute_amount_due(self):
for order in self:
order.amount_due = max(
(order.amount_deferred or 0.0) - (order.amount_settled or 0.0),
0.0,
)
@api.depends('amount_paid_cash')
def _compute_amount_paid_alias(self):
for order in self:
order.amount_paid = order.amount_paid_cash or 0.0
@api.depends('source_type')
def _compute_is_from_pos(self):
for order in self:
order.is_from_pos = order.source_type == 'pos'
@api.depends('source_type', 'state', 'manager_unlocked_until')
def _compute_locked(self):
now = fields.Datetime.now()
for order in self:
unlock_active = bool(
order.manager_unlocked_until
and order.manager_unlocked_until > now
)
base_locked = (
order.source_type == 'pos'
or order.state in FINAL_STATES
)
order.locked = base_locked and not unlock_active
# ── Lock enforcement helpers ──────────────────────────────────────
def _is_pos_sync(self):
"""True when the call originates from the POS sync hook (or any
explicit server path that opts in via the context flag).
Both create() and write() honour this so the bridge from
pos.order can build / refresh the laundry.order without fighting
its own lock guard. This is checked BEFORE anything else in
write() so indirect POS writes (stored-compute flushes, cascades)
can never raise from the guard.
"""
return bool(self.env.context.get(POS_SYNC_CTX))
def _check_lock_for_write(self, vals):
"""Raise UserError when `vals` would mutate a protected header
field on a currently-locked order. Workflow advances (state,
amount_settled, notes, manager_unlocked_*) are excluded by
whitelist (LOCKED_HEADER_FIELDS).
Note: the POS-sync bypass is already applied at the top of
`write()` — this helper is only invoked for non-bypassed paths.
"""
protected = LOCKED_HEADER_FIELDS.intersection(vals.keys())
if not protected:
return
for order in self:
if order.locked:
raise UserError(_(
'Order "%(name)s" is locked. Editable fields: state '
'transitions, internal notes, settlement amount.\n'
'To edit %(fields)s, ask a manager to use '
'"Unlock for Editing" first.',
name=order.name,
fields=', '.join(sorted(protected)),
))
def _check_lock_for_unlink(self):
"""POS-sourced and final-state orders cannot be unlinked. The
manager unlock wizard is intentionally NOT honored here — deletion
requires a stronger affordance (cancellation + audit trail), not
a temporary edit window.
"""
for order in self:
if order.source_type == 'pos':
raise UserError(_(
'Order "%(name)s" was created from POS and cannot '
'be deleted. Cancel the underlying POS order instead.',
name=order.name,
))
if order.state in FINAL_STATES:
raise UserError(_(
'Order "%(name)s" is in a final state (%(state)s) '
'and cannot be deleted.',
name=order.name,
state=dict(STATES).get(order.state, order.state),
))
@api.depends('line_ids.qty')
def _compute_item_count(self):
for order in self:
order.item_count = int(sum(order.line_ids.mapped('qty')))
# -- ORM --
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('name', 'New') == 'New':
vals['name'] = (
self.env['ir.sequence'].next_by_code('laundry.order')
or 'New'
)
self._apply_type_attribute_inference(vals)
return super().create(vals_list)
def write(self, vals):
# STEP 1 — POS sync bypass.
# Must be the very first thing we do. Any code path that opts
# into the context flag (POS create/sync, settlement engine,
# stored-compute flushes triggered from inside a bypassed write)
# MUST sail through unconditionally. No locking check, no
# side-effects, no iteration over self — just delegate to super.
# This guarantee is what makes POS payment/settlement flows
# immune to this model's lock guard.
if self._is_pos_sync():
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug(
'laundry.order.write BYPASS ids=%s keys=%s',
self.ids, list(vals.keys()),
)
return super().write(vals)
# STEP 2 — Forensic trace (DEBUG-level; off by default in prod,
# enable with `--log-level=debug` or `--log-handler=odoo.addons
# .laundry_management.models.laundry_order:DEBUG`).
if _logger.isEnabledFor(logging.DEBUG):
keys = list(vals.keys())
for order in self:
_logger.debug(
'laundry.order.write id=%s source=%s state=%s '
'locked=%s ctx_keys=%s vals_keys=%s',
order.id, order.source_type, order.state, order.locked,
sorted(self.env.context.keys()), keys,
)
# STEP 3 — Lock guard for non-POS callers.
self._check_lock_for_write(vals)
return super().write(vals)
def unlink(self):
# Explicit: even with POS-sync context, refuse to delete a locked
# order. Deletion is not a mutation the sync path ever issues.
self._check_lock_for_unlink()
return super().unlink()
@api.model
def _apply_type_attribute_inference(self, vals):
"""Fill in priority_level / is_delivery from the selected order
type and attributes when the caller did not explicitly set them.
Rules:
- type.priority='urgent' OR any attribute with
is_priority_related=True → priority_level='urgent'
- type.is_delivery=True OR any attribute with
is_delivery_related=True → is_delivery=True
- Do NOT overwrite explicit incoming delivery_address /
delivery_scheduled_at with blank values.
"""
type_id = vals.get('order_type_id')
order_type = (
self.env['laundry.order.type'].browse(type_id) if type_id else None
)
attribute_ids = []
raw_attrs = vals.get('attribute_ids') or []
for cmd in raw_attrs:
if isinstance(cmd, (list, tuple)) and len(cmd) >= 3:
# Odoo x2m commands: (6,0,[ids]), (4,id), etc.
if cmd[0] == 6 and isinstance(cmd[2], (list, tuple)):
attribute_ids.extend(cmd[2])
elif cmd[0] == 4 and cmd[1]:
attribute_ids.append(cmd[1])
elif isinstance(cmd, int):
attribute_ids.append(cmd)
attributes = (
self.env['laundry.order.attribute'].browse(attribute_ids)
if attribute_ids else self.env['laundry.order.attribute']
)
# Priority
if 'priority_level' not in vals:
urgent = (
(order_type and order_type.priority == 'urgent')
or any(a.is_priority_related for a in attributes)
)
vals['priority_level'] = 'urgent' if urgent else 'normal'
# Delivery
if 'is_delivery' not in vals:
delivery = (
(order_type and order_type.is_delivery)
or any(a.is_delivery_related for a in attributes)
)
vals['is_delivery'] = bool(delivery)
# -- Workflow actions --
def action_process(self):
for order in self:
if order.state != 'intake':
raise UserError(_(
'Order "%(name)s" is not in Intake state.',
name=order.name,
))
order.state = 'processing'
def action_ready(self):
for order in self:
if order.state != 'processing':
raise UserError(_(
'Order "%(name)s" is not in Processing state.',
name=order.name,
))
order.state = 'ready'
def action_deliver(self):
"""Guards: must be Ready + fully paid (amount_due == 0).
Also stamps `delivered_at` so the dashboard KPIs (avg processing
time, on-time delivery %) can be computed from real data instead
of the heuristic on `write_date`.
"""
for order in self:
if order.state != 'ready':
raise UserError(_(
'Order "%(name)s" must be Ready before delivery.',
name=order.name,
))
if order.amount_due > 0:
raise UserError(_(
'Order "%(name)s" has %(due).2f outstanding. '
'Collect payment in POS before delivery.',
name=order.name,
due=order.amount_due,
))
order.write({
'state': 'delivered',
'delivered_at': fields.Datetime.now(),
})
def action_cancel(self):
"""Cancel an order. Allowed for manual orders only — POS-sourced
orders must be voided through the POS workflow to keep the sale
and the operational record in sync.
"""
for order in self:
if order.source_type == 'pos':
raise UserError(_(
'Order "%(name)s" was created from POS and cannot '
'be cancelled here. Cancel the underlying POS order '
'instead.',
name=order.name,
))
if order.state in FINAL_STATES:
raise UserError(_(
'Order "%(name)s" is already %(state)s.',
name=order.name,
state=dict(STATES).get(order.state, order.state),
))
order.state = 'cancelled'
# -- Smart button --
def action_open_pos_order(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'res_model': 'pos.order',
'res_id': self.pos_order_id.id,
'view_mode': 'form',
'target': 'current',
}
# ═════════════════════════════════════════════════════════════════
# POS "Laundry Orders" popup — server-side RPCs
# ---------------------------------------------------------------
# These methods are the ONLY way the POS popup interacts with the
# model. Each action delegates to the corresponding workflow method
# (`action_process` / `action_ready` / `action_deliver`) which already
# enforces state + amount_due guards server-side. Direct writes to
# LOCKED_HEADER_FIELDS are NOT exposed here — the Phase 3 lock
# remains the sole authority for business-edit protection.
# ═════════════════════════════════════════════════════════════════
def _pos_allowed_actions(self):
"""Return the list of action keys the popup may render for this
order. Pure function of state + amount_due. Final states only
allow printing.
"""
self.ensure_one()
actions = ['print_work_order']
if self.state in FINAL_STATES:
return actions
if self.state == 'intake':
actions.append('start_processing')
elif self.state == 'processing':
actions.append('mark_ready')
elif self.state == 'ready':
if self.amount_due <= 0:
actions.append('deliver')
else:
actions.append('collect_payment')
return actions
def _pos_payment_state(self):
self.ensure_one()
if self.amount_due > 0:
return 'due'
if self.amount_deferred > 0 and self.amount_settled >= self.amount_deferred:
return 'settled'
if self.amount_deferred > 0:
return 'deferred'
return 'paid'
def _pos_payload(self):
"""Compact, UI-ready dict. Single source of truth for the popup
shape — every RPC returns exactly this structure."""
self.ensure_one()
names = self.line_ids.mapped('product_id.name')
if not names:
names = self.line_ids.mapped('description')
state_selection = dict(self._fields['state'].selection)
return {
'id': self.id,
'name': self.name,
'state': self.state,
'state_label': state_selection.get(self.state) or self.state,
'pos_reference': self.pos_reference or '',
'is_from_pos': self.is_from_pos,
'create_date': fields.Datetime.to_string(self.create_date) if self.create_date else False,
'item_count': int(self.item_count or 0),
'service_summary': ', '.join(dict.fromkeys(names))[:80],
'amount_total': self.amount_total,
'amount_paid': self.amount_paid_cash,
'amount_deferred': self.amount_deferred,
'amount_settled': self.amount_settled,
'amount_due': self.amount_due,
'payment_state': self._pos_payment_state(),
'is_delivery': self.is_delivery,
'allowed_actions': self._pos_allowed_actions(),
}
@api.model
def pos_search_customer_orders(self, partner_id, search_query=False, limit=20):
"""Partner-scoped search for the POS popup.
• Always filters by partner_id (no global search in this phase).
• Optional search_query ilike-matches on name / pos_reference /
partner_phone.
• Hard-capped at 50 rows regardless of the caller's limit.
"""
if not partner_id:
return []
limit = max(1, min(int(limit or 20), 50))
q = (search_query or '').strip()
domain = [('partner_id', '=', partner_id)]
if q:
domain = [
'&',
('partner_id', '=', partner_id),
'|', '|',
('name', 'ilike', q),
('pos_reference', 'ilike', q),
('partner_phone', 'ilike', q),
]
orders = self.search(
domain, order='create_date desc, id desc', limit=limit,
)
return [o._pos_payload() for o in orders]
def pos_action_start_processing(self):
"""POS popup: advance intake → processing. Returns refreshed payload."""
self.ensure_one()
self.action_process()
return self._pos_payload()
def pos_action_mark_ready(self):
"""POS popup: advance processing → ready. Returns refreshed payload."""
self.ensure_one()
self.action_ready()
return self._pos_payload()
def pos_action_deliver(self):
"""POS popup: advance ready → delivered. Returns refreshed payload.
`action_deliver` raises UserError when amount_due > 0 — the popup
surfaces that error; no client-side duplication of the rule.
"""
self.ensure_one()
self.action_deliver()
return self._pos_payload()
@api.model
def pos_get_thermal_data(self, order_id):
"""Build a self-contained payload for the thermal Work-Order
receipt rendered by `laundry_management.LaundryWorkOrderThermal`.
Independent from `_pos_payload` — that one is for the popup list
(compact); this one carries every line + delivery meta the
cashier needs on the printed slip.
"""
order = self.browse(int(order_id))
if not order.exists():
return False
state_label = dict(order._fields['state'].selection).get(
order.state, order.state,
)
return {
'id': order.id,
'name': order.name,
'state': order.state,
'state_label': state_label,
'payment_state': order._pos_payment_state(),
'pos_reference': order.pos_reference or '',
'partner_name': order.partner_id.name or '',
# `mobile` is provided by the optional `phone` add-on; fall
# back gracefully when it isn't present in the install.
'partner_phone': (
order.partner_id.phone
or getattr(order.partner_id, 'mobile', '')
or ''
),
'company_name': order.company_id.name or '',
'create_date': fields.Datetime.to_string(order.create_date),
'lines': [{
'qty': line.qty,
'description': (
line.description
or (line.product_id.name if line.product_id else '')
),
'price_unit': line.price_unit,
'subtotal': line.subtotal,
'tracking_code': line.tracking_code or '',
} for line in order.line_ids],
'item_count': int(order.item_count or 0),
'amount_total': order.amount_total,
'amount_paid': order.amount_paid_cash,
'amount_deferred': order.amount_deferred,
'amount_settled': order.amount_settled,
'amount_due': order.amount_due,
'is_delivery': order.is_delivery,
'delivery_address': order.delivery_address or '',
'delivery_scheduled_at': (
fields.Datetime.to_string(order.delivery_scheduled_at)
if order.delivery_scheduled_at else ''
),
'currency_symbol': order.currency_id.symbol or '',
'currency_position': order.currency_id.position or 'after',
}
def action_open_unlock_wizard(self):
"""Open the manager unlock wizard pre-filled with this order.
Access is enforced inside the wizard's action method (group
check + reason required), but we also short-circuit here so
the button itself is silent for non-managers.
"""
self.ensure_one()
if not self.locked:
raise UserError(_(
'Order "%s" is already editable.', self.name,
))
return {
'type': 'ir.actions.act_window',
'res_model': 'laundry.order.unlock.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_order_id': self.id,
},
}

View File

@@ -0,0 +1,62 @@
from odoo import models, fields, api
class LaundryOrderAttribute(models.Model):
"""Optional per-order badge (Urgent / Hanger / Fold / Delicate / ...).
Multi-selectable on a laundry order. Semantic flags drive behavior
without name matching, so admins can rename freely.
"""
_name = 'laundry.order.attribute'
_inherit = ['pos.load.mixin']
_description = 'Laundry Order Attribute'
_order = 'sequence, id'
name = fields.Char(string='Name', required=True, translate=True)
code = fields.Char(string='Code')
sequence = fields.Integer(default=10)
active = fields.Boolean(default=True)
color = fields.Char(string='Color')
icon_image = fields.Binary(string='Icon')
description = fields.Text(string='Description', translate=True)
extra_price = fields.Float(
string='Extra Price',
help='Reserved for future pricing rules. Not applied automatically.',
)
pos_available = fields.Boolean(string='Available in POS', default=True)
company_id = fields.Many2one(
'res.company', string='Company',
default=lambda self: self.env.company, index=True,
)
is_delivery_related = fields.Boolean(
string='Delivery Related',
help='Selecting this attribute marks the order as delivery and '
'triggers the delivery-details prompt.',
)
is_priority_related = fields.Boolean(
string='Priority Related',
help='Selecting this attribute promotes the order to urgent priority.',
)
@api.model
def _load_pos_data_domain(self, data, config):
return [
('pos_available', '=', True),
('active', '=', True),
'|',
('company_id', '=', False),
('company_id', 'in', config.company_id.ids),
]
@api.model
def _load_pos_data_fields(self, config):
return [
'id', 'name', 'code', 'sequence',
'color', 'description',
'extra_price',
'is_delivery_related', 'is_priority_related',
]

View File

@@ -0,0 +1,288 @@
import logging
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from .laundry_order import POS_SYNC_CTX
_logger = logging.getLogger(__name__)
LINE_STATES = [
('received', 'Received'),
('processing', 'Processing'),
('ready', 'Ready'),
('delivered', 'Delivered'),
]
# Line fields the lock protects. NOT included on purpose:
# - state (per-item workflow advance is always allowed)
# - customer_note (operator commentary)
# - tracking_code (auto-assigned; cannot change after create either way)
LOCKED_LINE_FIELDS = frozenset({
'product_id', 'description', 'qty', 'price_unit',
})
class LaundryOrderLine(models.Model):
"""Line item on a laundry order — maps from pos.order.line.
Each line carries a unique scannable tracking_code (barcode) and its
own per-item workflow state. The order-level state on laundry.order
remains the source of truth for financial gates; the per-line state
is an operational overlay that supports items moving through the
workflow at different speeds.
"""
_name = 'laundry.order.line'
_description = 'Laundry Order Line'
_order = 'order_id, id'
order_id = fields.Many2one(
'laundry.order', string='Order',
required=True, ondelete='cascade', index=True,
)
# Mirror order-level partner/state so list + kanban views can filter/group
# without costly cross-model joins.
order_partner_id = fields.Many2one(
related='order_id.partner_id', store=True, index=True, readonly=True,
)
order_state = fields.Selection(
related='order_id.state', store=True, index=True, readonly=True,
)
product_id = fields.Many2one(
'product.product', string='Product',
)
description = fields.Char(
string='Description',
)
qty = fields.Float(
string='Quantity',
default=1.0, digits=(10, 2),
)
price_unit = fields.Float(
string='Unit Price',
digits=(10, 2), readonly=True,
)
customer_note = fields.Char(
string='Customer Note',
)
subtotal = fields.Float(
string='Subtotal',
compute='_compute_subtotal',
store=True, digits=(10, 2),
)
# -- Per-item tracking --
tracking_code = fields.Char(
string='Tracking Code',
copy=False, readonly=True, index=True,
help='Unique scannable barcode for this item.',
)
state = fields.Selection(
LINE_STATES,
string='Item Status',
default='received',
required=True, copy=False, index=True,
)
_tracking_code_uniq = models.Constraint(
'UNIQUE(tracking_code)',
'Tracking code must be unique across all laundry items.',
)
@api.depends('qty', 'price_unit')
def _compute_subtotal(self):
for line in self:
line.subtotal = line.qty * line.price_unit
# Sequence code for the auto-generated tracking_code (barcode).
_TRACKING_SEQ_CODE = 'laundry.order.line.tracking'
@api.model
def _next_tracking_code(self):
"""Allocate a tracking_code that is GUARANTEED unique across the
existing laundry_order_line table.
Why this exists
───────────────
Postgres sequences are NON-transactional: a `nextval()` advances
the sequence even when the surrounding ORM transaction rolls
back. Repeated POS validates that fail (lock, missing partner,
anything) eat sequence values without consuming them in real
rows. Conversely, a partial reseed / data import that inserts
rows with manual tracking_codes leaves the sequence BEHIND the
table's MAX. Either way, `next_by_code()` can return a code
that already exists → UniqueViolation → POS sale silently
misses its laundry-order link (the savepoint in pos_order.py
catches the SQL error to protect the POS commit).
How this fixes it
─────────────────
On collision, repair the sequence to (max_tracking_num + 1)
using a direct SQL nextval-skip, then ask the sequence again.
Capped at a few attempts so a real bug (e.g. malformed schema)
still surfaces instead of looping forever.
"""
seq = self.env['ir.sequence']
for attempt in range(5):
code = seq.next_by_code(self._TRACKING_SEQ_CODE) or False
if not code:
return False
# Cheap collision check; the underlying UNIQUE constraint is
# the real safety net — this just avoids paying the round-trip.
existing = self.sudo().search_count([('tracking_code', '=', code)])
if not existing:
return code
# Collision — repair sequence past the current MAX, then retry.
self._repair_tracking_sequence()
_logger.warning(
"laundry.order.line tracking sequence collided on %s "
"(attempt %d); repaired and retrying.", code, attempt + 1,
)
# If we still can't get a unique code after 5 tries, surface the
# problem instead of writing an empty code.
raise UserError(_(
'Could not allocate a unique tracking code after 5 attempts. '
'Check the laundry_management sequence configuration.'
))
@api.model
def _repair_tracking_sequence(self):
"""Advance the tracking-code sequence past the actual MAX in the
table. Idempotent — safe to call repeatedly. SQL-level so it
works even when the ORM env context is unusual (sudo, sync hook).
"""
self.env.cr.execute("""
SELECT COALESCE(MAX(
CAST(NULLIF(REGEXP_REPLACE(tracking_code, '[^0-9]', '', 'g'), '')
AS INTEGER)
), 0)
FROM laundry_order_line
WHERE tracking_code IS NOT NULL;
""")
max_existing = self.env.cr.fetchone()[0] or 0
# `ir.sequence` writes update number_next; we use the API for
# safety (handles ranges, prefixes, padding).
seq = self.env['ir.sequence'].sudo().search(
[('code', '=', self._TRACKING_SEQ_CODE)], limit=1,
)
if seq:
seq.write({'number_next': max_existing + 1})
@api.model_create_multi
def create(self, vals_list):
# POS-sync bypass FIRST — POS creates the order and its lines in a
# single create_vals payload; both must sail through the guard.
pos_sync = bool(self.env.context.get(POS_SYNC_CTX))
if not pos_sync:
order_ids = {v.get('order_id') for v in vals_list if v.get('order_id')}
if order_ids:
orders = self.env['laundry.order'].browse(list(order_ids))
for order in orders:
if order.locked:
raise UserError(_(
'Cannot add a line to locked order "%s".',
order.name,
))
for vals in vals_list:
if not vals.get('tracking_code'):
vals['tracking_code'] = self._next_tracking_code()
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug(
'laundry.order.line.create pos_sync=%s count=%d',
pos_sync, len(vals_list),
)
return super().create(vals_list)
def write(self, vals):
if self.env.context.get(POS_SYNC_CTX):
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug(
'laundry.order.line.write BYPASS ids=%s keys=%s',
self.ids, list(vals.keys()),
)
return super().write(vals)
protected = LOCKED_LINE_FIELDS.intersection(vals.keys())
if protected:
for line in self:
if line.order_id.locked:
raise UserError(_(
'Line on locked order "%(order)s" cannot edit '
'%(fields)s. Ask a manager to use "Unlock for '
'Editing" first.',
order=line.order_id.name,
fields=', '.join(sorted(protected)),
))
return super().write(vals)
def unlink(self):
# No bypass: deletion is never issued by the POS sync path.
for line in self:
if line.order_id.locked:
raise UserError(_(
'Cannot delete a line from locked order "%s".',
line.order_id.name,
))
return super().unlink()
# -- Per-line workflow actions (1-click) --
def action_line_process(self):
for line in self:
if line.state != 'received':
raise UserError(_(
'Item %(code)s is not in Received state.',
code=line.tracking_code or line.id,
))
line.state = 'processing'
def action_line_ready(self):
for line in self:
if line.state != 'processing':
raise UserError(_(
'Item %(code)s is not in Processing state.',
code=line.tracking_code or line.id,
))
line.state = 'ready'
def action_line_deliver(self):
for line in self:
if line.state != 'ready':
raise UserError(_(
'Item %(code)s must be Ready before delivery.',
code=line.tracking_code or line.id,
))
line.state = 'delivered'
@api.model
def action_scan_advance(self, tracking_code):
"""Advance an item one stage by its scanned tracking code.
Intended for barcode scanner workflow: scanner types the code,
this method finds the line and bumps its state to the next stage.
Returns the new state or raises UserError if terminal / unknown.
"""
if not tracking_code:
raise UserError(_('Scan a tracking code.'))
line = self.search([('tracking_code', '=', tracking_code.strip())], limit=1)
if not line:
raise UserError(_('No item with tracking code %s.', tracking_code))
transitions = {
'received': line.action_line_process,
'processing': line.action_line_ready,
'ready': line.action_line_deliver,
}
action = transitions.get(line.state)
if not action:
raise UserError(_(
'Item %(code)s is already %(state)s.',
code=line.tracking_code, state=line.state,
))
action()
return {
'id': line.id,
'tracking_code': line.tracking_code,
'state': line.state,
'order_name': line.order_id.name,
'partner_name': line.order_partner_id.name,
'product_name': line.product_id.display_name,
}

View File

@@ -0,0 +1,53 @@
from odoo import models, fields, api
class LaundryOrderLineAddon(models.Model):
"""Optional add-on service attached to one sale.order.line.
Examples per item:
- Express handling (+10 SAR)
- Starch / تقطير (+3 SAR)
- Packaging (+2 SAR)
- Perfume scent (+5 SAR)
The parent line's subtotal does NOT automatically include add-on prices
(sale.order.line controls its own subtotal). Add-ons are tracked here for
printing and reporting purposes; staff can create a separate order line for
the add-on product if billing is required.
"""
_name = 'laundry.order.line.addon'
_description = 'Order Line Add-on'
_order = 'line_id, id'
line_id = fields.Many2one(
'sale.order.line', string='Order Line',
required=True, ondelete='cascade', index=True,
)
# Denormal for easy reporting — derived from sale.order.line.order_id
order_id = fields.Many2one(
'sale.order',
related='line_id.order_id', store=True, index=True,
)
name = fields.Char(
string='Add-on / الإضافة',
required=True,
help='Name of the additional service (e.g. Express, Starch, Packaging).',
)
price = fields.Float(
string='Price / السعر',
required=True, digits=(10, 2), default=0.0,
)
quantity = fields.Float(
string='Qty',
default=1.0, digits=(10, 2),
)
subtotal = fields.Float(
string='Subtotal',
compute='_compute_subtotal', store=True, digits=(10, 2),
)
@api.depends('price', 'quantity')
def _compute_subtotal(self):
for addon in self:
addon.subtotal = addon.price * addon.quantity

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