Bug 30518: Correct DateTime maths for needs_advancing
[koha.git] / Koha / Charges / Sales.pm
1 package Koha::Charges::Sales;
2
3 # Copyright 2019 PTFS Europe
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Koha::Account::Lines;
23 use Koha::Account::DebitTypes;
24 use Koha::Account::Offsets;
25 use Koha::DateUtils qw( dt_from_string );
26 use Koha::Exceptions;
27
28 =head1 NAME
29
30 Koha::Charges::Sale - Module for collecting sales in Koha
31
32 =head1 SYNOPSIS
33
34   use Koha::Charges::Sale;
35
36   my $sale =
37     Koha::Charges::Sale->new( { cash_register => $register, staff_id => $staff_id } );
38   $sale->add_item($item);
39   $sale->purchase( { payment_type => 'CASH' } );
40
41 =head2 Class methods
42
43 =head3 new
44
45   Koha::Charges::Sale->new(
46     {
47         cash_register  => $cash_register,
48         staff_id        => $staff_id,
49         [ payment_type => $payment_type ],
50         [ items        => $items ],
51         [ patron       => $patron ],
52     }
53   );
54
55 =cut
56
57 sub new {
58     my ( $class, $params ) = @_;
59
60     Koha::Exceptions::MissingParameter->throw(
61         "Missing mandatory parameter: cash_register")
62       unless $params->{cash_register};
63
64     Koha::Exceptions::MissingParameter->throw(
65         "Missing mandatory parameter: staff_id")
66       unless $params->{staff_id};
67
68     Carp::confess("Key 'cash_register' is not a Koha::Cash::Register object!")
69       unless $params->{cash_register}->isa('Koha::Cash::Register');
70
71     return bless( $params, $class );
72 }
73
74 =head3 payment_type
75
76   my $payment_type = $sale->payment_type( $payment_type );
77
78 A getter/setter for this instances associated payment type.
79
80 =cut
81
82 sub payment_type {
83     my ( $self, $payment_type ) = @_;
84
85     if ($payment_type) {
86         Koha::Exceptions::Account::UnrecognisedType->throw(
87             error => 'Type of payment not recognised' )
88           unless ( exists( $self->_get_valid_payments->{$payment_type} ) );
89
90         $self->{payment_type} = $payment_type;
91     }
92
93     return $self->{payment_type};
94 }
95
96 =head3 _get_valid_payments
97
98   my $valid_payments = $sale->_get_valid_payments;
99
100 A getter which returns a hashref whose keys represent valid payment types.
101
102 =cut
103
104 sub _get_valid_payments {
105     my $self = shift;
106
107     $self->{valid_payments} //= {
108         map { $_ => 1 } Koha::AuthorisedValues->search_with_library_limits(
109             {
110                 category => 'PAYMENT_TYPE'
111             },
112             {},
113             $self->{cash_register}->branch # filter by cash_register branch
114         )->get_column('authorised_value')
115     };
116
117     return $self->{valid_payments};
118 }
119
120 =head3 add_item
121
122   my $item = { price => 0.25, quantity => 1, code => 'COPY' };
123   $sale->add_item( $item );
124
125 =cut
126
127 sub add_item {
128     my ( $self, $item ) = @_;
129
130     Koha::Exceptions::MissingParameter->throw(
131         "Missing mandatory parameter: code")
132       unless $item->{code};
133
134     Koha::Exceptions::Account::UnrecognisedType->throw(
135         error => 'Type of debit not recognised' )
136       unless ( exists( $self->_get_valid_items->{ $item->{code} } ) );
137
138     Koha::Exceptions::MissingParameter->throw(
139         "Missing mandatory parameter: price")
140       unless $item->{price};
141
142     Koha::Exceptions::MissingParameter->throw(
143         "Missing mandatory parameter: quantity")
144       unless $item->{quantity};
145
146     push @{ $self->{items} }, $item;
147     return $self;
148 }
149
150 =head3 _get_valid_items
151
152   my $valid_items = $sale->_get_valid_items;
153
154 A getter which returns a hashref whose keys represent valid sale items.
155
156 =cut
157
158 sub _get_valid_items {
159     my $self = shift;
160
161     $self->{valid_items} //= {
162         map { $_ => 1 }
163           Koha::Account::DebitTypes->search_with_library_limits( {}, {},
164             $self->{cash_register}->branch )->get_column('code')
165     };
166
167     return $self->{valid_items};
168 }
169
170 =head3 purchase
171
172   my $credit_line = $sale->purchase;
173
174 =cut
175
176 sub purchase {
177     my ( $self, $params ) = @_;
178
179     if ( $params->{payment_type} ) {
180         Koha::Exceptions::Account::UnrecognisedType->throw(
181             error => 'Type of payment not recognised' )
182           unless (
183             exists( $self->_get_valid_payments->{ $params->{payment_type} } ) );
184
185         $self->{payment_type} = $params->{payment_type};
186     }
187
188     Koha::Exceptions::MissingParameter->throw(
189         "Missing mandatory parameter: payment_type")
190       unless $self->{payment_type};
191
192     Koha::Exceptions::NoChanges->throw(
193         "Cannot purchase before calling add_item")
194       unless $self->{items};
195
196     my $schema     = Koha::Database->new->schema;
197     my $dt         = dt_from_string();
198     my $total_owed = 0;
199     my $payment;
200
201     $schema->txn_do(
202         sub {
203
204             # Add accountlines for each item being purchased
205             my $debits;
206             for my $item ( @{ $self->{items} } ) {
207
208                 my $amount = $item->{quantity} * $item->{price};
209                 $total_owed = $total_owed + $amount;
210
211                 # Insert the account line
212                 my $debit = Koha::Account::Line->new(
213                     {
214                         amount            => $amount,
215                         debit_type_code   => $item->{code},
216                         amountoutstanding => $amount,
217                         note              => $item->{quantity},
218                         manager_id        => $self->{staff_id},
219                         interface         => 'intranet',
220                         branchcode        => $self->{cash_register}->branch,
221                         date              => $dt
222                     }
223                 )->store();
224                 push @{$debits}, $debit;
225
226                 # Record the account offset
227                 my $account_offset = Koha::Account::Offset->new(
228                     {
229                         debit_id => $debit->id,
230                         type     => 'CREATE',
231                         amount   => $amount
232                     }
233                 )->store();
234             }
235
236             # Add accountline for payment
237             $payment = Koha::Account::Line->new(
238                 {
239                     amount            => 0 - $total_owed,
240                     credit_type_code  => 'PURCHASE',
241                     payment_type      => $self->{payment_type},
242                     amountoutstanding => 0 - $total_owed,
243                     manager_id        => $self->{staff_id},
244                     interface         => 'intranet',
245                     branchcode        => $self->{cash_register}->branch,
246                     register_id       => $self->{cash_register}->id,
247                     date              => $dt,
248                     note              => "POS SALE"
249                 }
250             )->store();
251
252             # Record the account offset
253             my $payment_offset = Koha::Account::Offset->new(
254                 {
255                     credit_id => $payment->id,
256                     type      => 'CREATE',
257                     amount    => $payment->amount
258                 }
259             )->store();
260
261             # Link payment to charges
262             $payment->apply( { debits => $debits } );
263             $payment->discard_changes;
264         }
265     );
266
267     return $payment;
268 }
269
270 =head1 AUTHOR
271
272 Martin Renvoize <martin.renvoize@ptfs-europe.com>
273
274 =cut
275
276 1;