Revision 06ceb72f
Von Kivitendo Admin vor mehr als 1 Jahr hinzugefügt
SL/Clearing.pm | ||
---|---|---|
1 |
package SL::Clearing; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use warnings; |
|
5 |
|
|
6 |
use SL::DB::Employee; |
|
7 |
use SL::DB::Cleared; |
|
8 |
use SL::DB::ClearedGroup; |
|
9 |
use SL::DBUtils; |
|
10 |
|
|
11 |
sub create_cleared_group { |
|
12 |
my ($acc_trans_ids) = @_; |
|
13 |
|
|
14 |
my @acc_trans_ids = @$acc_trans_ids; |
|
15 |
|
|
16 |
die "need at least 2 acc_trans entries" unless scalar @acc_trans_ids > 1; |
|
17 |
|
|
18 |
my $db = SL::DB->client; |
|
19 |
|
|
20 |
$db->with_transaction(sub { |
|
21 |
# check that |
|
22 |
# * sum of selected acc_trans amounts is 0 |
|
23 |
# * there is more than 1 acc_trans |
|
24 |
# * they are all for the same chart |
|
25 |
# * and the chart is configured for clearing |
|
26 |
# * none of the transactions have been cleared yet |
|
27 |
# |
|
28 |
# to save db calls or later calculations in code, do this is all with one query |
|
29 |
|
|
30 |
my $query = <<SQL; |
|
31 |
with selected_acc_trans as ( |
|
32 |
select a.amount, a.chart_id, |
|
33 |
c.clearing, |
|
34 |
cl.cleared_group_id |
|
35 |
from acc_trans a |
|
36 |
left join chart c on (c.id = a.chart_id) |
|
37 |
left join cleared cl on (cl.acc_trans_id = a.acc_trans_id) |
|
38 |
where a.acc_trans_id = any(?) |
|
39 |
), |
|
40 |
sum_and_count as ( |
|
41 |
select count(amount) as count, |
|
42 |
sum(amount) as sum |
|
43 |
from selected_acc_trans |
|
44 |
), |
|
45 |
all_transactions_uncleared as ( |
|
46 |
select case when count(*) > 0 |
|
47 |
then false |
|
48 |
else true |
|
49 |
end as all_transactions_uncleared |
|
50 |
from selected_acc_trans |
|
51 |
where cleared_group_id is not null |
|
52 |
), |
|
53 |
distinct_charts as ( |
|
54 |
select count(distinct chart_id) as number_of_distinct_charts |
|
55 |
from selected_acc_trans |
|
56 |
), |
|
57 |
all_charts_have_clearing as ( |
|
58 |
select case when count(*) > 0 |
|
59 |
then false |
|
60 |
else true |
|
61 |
end as all_charts_have_clearing |
|
62 |
from selected_acc_trans |
|
63 |
where clearing is false |
|
64 |
) |
|
65 |
select ( select sum from sum_and_count ), |
|
66 |
( select count from sum_and_count ), |
|
67 |
( select all_transactions_uncleared from all_transactions_uncleared ), |
|
68 |
( select number_of_distinct_charts from distinct_charts ), |
|
69 |
( select all_charts_have_clearing from all_charts_have_clearing ) |
|
70 |
SQL |
|
71 |
my ($sum, $count, $all_transactions_uncleared, $number_of_distinct_charts, $all_charts_have_clearing) |
|
72 |
= selectfirst_array_query($::form, $db->dbh, $query, \@acc_trans_ids); |
|
73 |
|
|
74 |
die "clearing error: sum isn't 0" unless $sum == 0; |
|
75 |
die "clearing error: need to select more than one transaction" unless $count > 1; |
|
76 |
die "clearing error: no acc_trans selected" unless $count > 1; |
|
77 |
die "clearing error: some bookings have already been cleared" unless $all_transactions_uncleared; |
|
78 |
die "clearing error: all bookings must be for the same chart" unless $number_of_distinct_charts == 1; |
|
79 |
die "clearing error: can only clear bookings for charts that are configured for clearing" unless $all_charts_have_clearing; |
|
80 |
|
|
81 |
my $cg = SL::DB::ClearedGroup->new( |
|
82 |
employee_id => SL::DB::Manager::Employee->current->id, |
|
83 |
)->save; |
|
84 |
|
|
85 |
foreach my $acc_trans_id ( @acc_trans_ids ) { |
|
86 |
SL::DB::Cleared->new( |
|
87 |
acc_trans_id => $acc_trans_id, |
|
88 |
cleared_group_id => $cg->id, |
|
89 |
)->save; |
|
90 |
} |
|
91 |
return $cg; |
|
92 |
}) or do { |
|
93 |
die "error while saving cleared_group: " . $@; |
|
94 |
}; |
|
95 |
} |
|
96 |
|
|
97 |
sub remove_cleared_group { |
|
98 |
my ($cleared_group_id) = @_; |
|
99 |
|
|
100 |
my $result = SL::DB::ClearedGroup->new(id => $cleared_group_id)->delete; |
|
101 |
$result ? return 1 : die "error while deleting cleared_group"; |
|
102 |
} |
|
103 |
|
|
104 |
sub load_chart_transactions { |
|
105 |
my ($params) = @_; |
|
106 |
|
|
107 |
# possible params: |
|
108 |
# |
|
109 |
# chart_id (necessary) |
|
110 |
# fromdate |
|
111 |
# todate |
|
112 |
# project_id |
|
113 |
# department_id |
|
114 |
# load_cleared |
|
115 |
|
|
116 |
die "missing chart_id param" unless $params->{chart_id}; |
|
117 |
my $dbh = SL::DB->client->dbh; |
|
118 |
|
|
119 |
my %params = %{$params}; |
|
120 |
|
|
121 |
my @sql_params = delete $params{"chart_id"}; |
|
122 |
|
|
123 |
my $WHERE = ''; |
|
124 |
|
|
125 |
# only load cleared bookings if this is explicitly desired |
|
126 |
if ( $params{"load_cleared"} ) { |
|
127 |
delete $params{"load_cleared"}; |
|
128 |
} else { |
|
129 |
$WHERE .= ' and cl.cleared_group_id is null '; |
|
130 |
}; |
|
131 |
|
|
132 |
if ( $params{"fromdate"} && ref($params{"fromdate"}) eq 'DateTime' ) { |
|
133 |
push(@sql_params, delete $params{"fromdate"}); |
|
134 |
$WHERE .= ' and a.transdate >= ? ' |
|
135 |
} |
|
136 |
|
|
137 |
if ( $params{"todate"} && ref($params{"todate"}) eq 'DateTime' ) { |
|
138 |
push(@sql_params, delete $params{"todate"}); |
|
139 |
$WHERE .= ' and a.transdate <= ? ' |
|
140 |
} |
|
141 |
|
|
142 |
my $PROJECT_WHERE = ''; |
|
143 |
if ( $params{"project_id"} ) { |
|
144 |
$PROJECT_WHERE = ' and coalesce(a.project_id, ap.globalproject_id, ar.globalproject_id) = ? '; |
|
145 |
push(@sql_params, delete $params{"project_id"}); |
|
146 |
} |
|
147 |
|
|
148 |
my $DEPARTMENT_WHERE = ''; |
|
149 |
if ( $params{"department_id"} ) { |
|
150 |
$DEPARTMENT_WHERE = ' and coalesce(ap.department_id, ar.department_id, gl.department_id) = ? '; |
|
151 |
push(@sql_params, delete $params{"department_id"}); |
|
152 |
} |
|
153 |
|
|
154 |
# if ( keys %params) { |
|
155 |
# # hash not empty, log it |
|
156 |
# $main::lxdebug->dump(0, "found illegal params in Clearing load_data", \%params); |
|
157 |
# } |
|
158 |
|
|
159 |
# limit number of transactions to be loaded, so you don't overwhelm the |
|
160 |
# interface if you forget to set dates |
|
161 |
my $LIMIT = 1500; # TODO: better way of dealing with limit. |
|
162 |
|
|
163 |
my $sql = <<"SQL"; |
|
164 |
select a.acc_trans_id, |
|
165 |
a.itime, |
|
166 |
a.amount, a.transdate, |
|
167 |
case when a.amount > 0 then a.amount else null end as credit, |
|
168 |
case when a.amount < 0 then a.amount * -1 else null end as debit, |
|
169 |
c.accno, c.description, |
|
170 |
p.id as project_id, p.projectnumber, p.description as projectdescription, |
|
171 |
case when cl.acc_trans_id is not null then true else false end as cleared, |
|
172 |
cl.cleared_group_id, |
|
173 |
e.name as employee, |
|
174 |
e.id as employee_id, |
|
175 |
coalesce(gl.reference, ar.invnumber, ap.invnumber) as reference, |
|
176 |
case when gl.id is not null then 'gl' |
|
177 |
when ar.id is not null then 'ar' |
|
178 |
else 'ap' |
|
179 |
end as record_type, |
|
180 |
gegen_chart_accnos.accnos as gegen_chart_accnos |
|
181 |
from acc_trans a |
|
182 |
left join chart c on (c.id = a.chart_id) |
|
183 |
left join gl on (gl.id = a.trans_id) |
|
184 |
left join ar on (ar.id = a.trans_id) |
|
185 |
left join ap on (ap.id = a.trans_id) |
|
186 |
left join project p on (p.id = coalesce(a.project_id, ap.globalproject_id, ar.globalproject_id)) |
|
187 |
left join employee e on (coalesce(gl.employee_id, ar.employee_id, ap.employee_id) = e.id) |
|
188 |
left join cleared cl on (cl.acc_trans_id = a.acc_trans_id) |
|
189 |
left join lateral ( |
|
190 |
select string_agg(chart.accno, ', ' ) as accnos |
|
191 |
from acc_trans |
|
192 |
left join chart on (chart.id = acc_trans.chart_id) |
|
193 |
where trans_id = a.trans_id and acc_trans_id != a.acc_trans_id |
|
194 |
and sign(a.amount) != sign(amount) -- ignore charts of opposite sign |
|
195 |
) gegen_chart_accnos on true |
|
196 |
where c.id = ? |
|
197 |
$WHERE |
|
198 |
$PROJECT_WHERE |
|
199 |
$DEPARTMENT_WHERE |
|
200 |
order by a.transdate |
|
201 |
limit $LIMIT |
|
202 |
SQL |
|
203 |
|
|
204 |
selectall_hashref_query($::form, $dbh, $sql, @sql_params); |
|
205 |
} |
|
206 |
|
|
207 |
sub load_cleared_group_transactions_by_group_id { |
|
208 |
my ($cleared_group_id) = @_; |
|
209 |
|
|
210 |
my $dbh = SL::DB->client->dbh; |
|
211 |
|
|
212 |
my $sql = <<"SQL"; |
|
213 |
select a.acc_trans_id, |
|
214 |
a.amount, a.transdate, |
|
215 |
case when a.amount > 0 then a.amount else null end as credit, |
|
216 |
case when a.amount < 0 then a.amount * -1 else null end as debit, |
|
217 |
c.accno, c.description, |
|
218 |
case when cl.acc_trans_id is not null then true else false end as cleared, |
|
219 |
cl.cleared_group_id, |
|
220 |
cg.itime, |
|
221 |
e.name as employee, |
|
222 |
e.id as employee_id, |
|
223 |
coalesce(gl.reference, ar.invnumber, ap.invnumber) as reference, |
|
224 |
case when gl.id is not null then 'gl' |
|
225 |
when ar.id is not null then 'ar' |
|
226 |
else 'ap' |
|
227 |
end as record_type, |
|
228 |
gegen_chart_accnos.accnos as gegen_chart_accnos |
|
229 |
from cleared cl |
|
230 |
left join cleared_group cg on (cg.id = cl.cleared_group_id) |
|
231 |
left join acc_trans a on (cl.acc_trans_id = a.acc_trans_id) |
|
232 |
left join chart c on (c.id = a.chart_id) |
|
233 |
left join gl on (gl.id = a.trans_id) |
|
234 |
left join ar on (ar.id = a.trans_id) |
|
235 |
left join ap on (ap.id = a.trans_id) |
|
236 |
left join employee e on (cg.employee_id = e.id ) -- employee is the employee who cleared it |
|
237 |
left join lateral ( |
|
238 |
select string_agg(chart.accno, ', ' ) as accnos |
|
239 |
from acc_trans |
|
240 |
left join chart on (chart.id = acc_trans.chart_id) |
|
241 |
where trans_id = a.trans_id and acc_trans_id != a.acc_trans_id |
|
242 |
and sign(a.amount) != sign(amount) -- ignore bookings of opposite sign |
|
243 |
) gegen_chart_accnos on true |
|
244 |
where cl.cleared_group_id = ? |
|
245 |
order by a.transdate |
|
246 |
SQL |
|
247 |
my $data = selectall_hashref_query($::form, $dbh, $sql, $cleared_group_id); |
|
248 |
return $data; |
|
249 |
} |
|
250 |
|
|
251 |
1; |
Auch abrufbar als: Unified diff
Konten ausziffern - Ausziffergruppen erstellen und löschen