00399029b094b210189bf2536edce2958dc88663
[kivitendo-erp.git] / SL / DB / ShopOrder.pm
1 # This file has been auto-generated only because it didn't exist.
2 # Feel free to modify it at will; it will not be overwritten automatically.
3
4 package SL::DB::ShopOrder;
5
6 use strict;
7
8 use SL::DBUtils;
9 use SL::DB::Shop;
10 use SL::DB::MetaSetup::ShopOrder;
11 use SL::DB::Manager::ShopOrder;
12 use SL::DB::PaymentTerm;
13 use SL::DB::Helper::LinkedRecords;
14 use SL::Locale::String qw(t8);
15 use Carp;
16
17 __PACKAGE__->meta->add_relationships(
18   shop_order_items => {
19     class      => 'SL::DB::ShopOrderItem',
20     column_map => { id => 'shop_order_id' },
21     type       => 'one to many',
22   },
23 );
24
25 __PACKAGE__->meta->initialize;
26
27 sub convert_to_sales_order {
28   my ($self, %params) = @_;
29
30   my $customer  = delete $params{customer};
31   my $employee  = delete $params{employee};
32   my $transdate = delete $params{transdate} // DateTime->today_local;
33   croak "param customer is missing" unless ref($customer) eq 'SL::DB::Customer';
34   croak "param employee is missing" unless ref($employee) eq 'SL::DB::Employee';
35
36   require SL::DB::Order;
37   require SL::DB::OrderItem;
38   require SL::DB::Part;
39   require SL::DB::Shipto;
40   my @error_report;
41
42   my @items = map{
43
44     my $part = SL::DB::Manager::Part->find_by(partnumber => $_->partnumber);
45
46     unless($part){
47       push @error_report, t8('Part with partnumber: #1 not found', $_->partnumber);
48     }else{
49       my $current_order_item = SL::DB::OrderItem->new(
50         parts_id            => $part->id,
51         description         => $_->description, # description from the shop
52         longdescription     => $part->notes,    # longdescription from parts. TODO locales
53         qty                 => $_->quantity,
54         sellprice           => $_->price,
55         unit                => $part->unit,
56         position            => $_->position,
57         active_price_source => $_->active_price_source,
58       );
59     }
60   }@{ $self->shop_order_items };
61
62   if(!scalar(@error_report)){
63
64     my $shipto_id;
65     if ($self->has_differing_delivery_address) {
66       if(my $address = SL::DB::Manager::Shipto->find_by( shiptoname   => $self->delivery_fullname,
67                                                          shiptostreet => $self->delivery_street,
68                                                          shiptocity   => $self->delivery_city,
69                                                         )) {
70         $shipto_id = $address->{shipto_id};
71       } else {
72         my $deliveryaddress = SL::DB::Shipto->new;
73         $deliveryaddress->assign_attributes(
74           shiptoname         => $self->delivery_fullname,
75           shiptodepartment_1 => $self->delivery_company,
76           shiptodepartment_2 => $self->delivery_department,
77           shiptostreet       => $self->delivery_street,
78           shiptozipcode      => $self->delivery_zipcode,
79           shiptocity         => $self->delivery_city,
80           shiptocountry      => $self->delivery_country,
81           trans_id           => $customer->id,
82           module             => "CT",
83         );
84         $deliveryaddress->save;
85         $shipto_id = $deliveryaddress->{shipto_id};
86       }
87     }
88
89     my $shop = SL::DB::Manager::Shop->find_by(id => $self->shop_id);
90     my $order = SL::DB::Order->new(
91       amount                  => $self->amount,
92       cusordnumber            => $self->shop_ordernumber,
93       customer_id             => $customer->id,
94       shipto_id               => $shipto_id,
95       orderitems              => [ @items ],
96       employee_id             => $employee->id,
97       intnotes                => $customer->notes,
98       salesman_id             => $employee->id,
99       taxincluded             => $self->tax_included,
100       payment_id              => $self->payment_id,
101       taxzone_id              => $customer->taxzone_id,
102       currency_id             => $customer->currency_id,
103       transaction_description => $shop->transaction_description,
104       transdate               => $transdate,
105     );
106      return $order;
107    }else{
108      my %error_order = (error   => 1,
109                         errors  => [ @error_report ],
110                        );
111      return \%error_order;
112    }
113 };
114
115 sub check_for_existing_customers {
116   my ($self, %params) = @_;
117   my $customers;
118
119   my $name             = $self->billing_lastname ne '' ? $self->billing_firstname . " " . $self->billing_lastname : '';
120   my $lastname         = $self->billing_lastname ne '' ? "%" . $self->billing_lastname . "%"                      : '';
121   my $company          = $self->billing_company  ne '' ? "%" . $self->billing_company  . "%"                      : '';
122   my $street           = $self->billing_street   ne '' ?  $self->billing_street                                   : '';
123   my $street_not_fuzzy = $self->billing_street   ne '' ?  "%" . $self->billing_street . "%"                       : '';
124   my $zipcode          = $self->billing_street   ne '' ?  $self->billing_zipcode                                  : '';
125   my $email            = $self->billing_street   ne '' ?  $self->billing_email                                    : '';
126
127   if(check_trgm($::form->get_standard_dbh())) {
128     # Fuzzysearch for street to find e.g. "Dorfstrasse - Dorfstr. - Dorfstraße"
129     my $fs_query = <<SQL;
130 SELECT *
131 FROM customer
132 WHERE (
133    (
134     ( name ILIKE ? OR name ILIKE ? )
135       AND
136     zipcode ILIKE ?
137    )
138  OR
139    ( street % ?  AND zipcode ILIKE ?)
140  OR
141    email ILIKE ?
142 ) AND obsolete = 'F'
143 SQL
144
145     my @values = ($lastname, $company, $self->billing_zipcode, $street, $self->billing_zipcode, $self->billing_email);
146
147     $customers = SL::DB::Manager::Customer->get_objects_from_sql(
148       sql  => $fs_query,
149       args => \@values,
150     );
151   }else{
152     # If trgm extension is not installed
153     $customers = SL::DB::Manager::Customer->get_all(
154       where => [
155           or => [
156             and => [
157                      or => [ 'name' => { ilike => $lastname },
158                              'name' => { ilike => $company  },
159                            ],
160                      'zipcode' => { ilike => $zipcode },
161                    ],
162             and => [
163                      and => [ 'street'  => { ilike => $street_not_fuzzy },
164                               'zipcode' => { ilike => $zipcode },
165                             ],
166                    ],
167             or  => [ 'email' => { ilike => $email } ],
168           ],
169       ],
170     );
171   }
172
173   return $customers;
174 }
175
176 sub check_for_open_invoices {
177   my ($self) = @_;
178     my $open_invoices = SL::DB::Manager::Invoice->get_all_count(
179       query => [customer_id => $self->{kivi_customer_id},
180               paid => {lt_sql => 'amount'},
181       ],
182     );
183   return $open_invoices;
184 }
185
186 sub get_customer{
187   my ($self, %params) = @_;
188   my $shop = SL::DB::Manager::Shop->find_by(id => $self->shop_id);
189   my $customer_proposals = $self->check_for_existing_customers;
190   my $name = $self->billing_firstname . " " . $self->billing_lastname;
191   my $customer = 0;
192   my $default_payment    = SL::DB::Manager::PaymentTerm->get_first();
193   my $payment_id = $default_payment ? $default_payment->id : undef;
194   if(!scalar(@{$customer_proposals})){
195     my %address = ( 'name'                  => $name,
196                     'department_1'          => $self->billing_company,
197                     'department_2'          => $self->billing_department,
198                     'street'                => $self->billing_street,
199                     'zipcode'               => $self->billing_zipcode,
200                     'city'                  => $self->billing_city,
201                     'email'                 => $self->billing_email,
202                     'country'               => $self->billing_country,
203                     'greeting'              => $self->billing_greeting,
204                     'fax'                   => $self->billing_fax,
205                     'phone'                 => $self->billing_phone,
206                     'ustid'                 => $self->billing_vat,
207                     'taxincluded_checked'   => $shop->pricetype eq "brutto" ? 1 : 0,
208                     'taxincluded'           => $shop->pricetype eq "brutto" ? 1 : 0,
209                     'pricegroup_id'         => (split '\/',$shop->price_source)[0] eq "pricegroup" ?  (split '\/',$shop->price_source)[1] : undef,
210                     'taxzone_id'            => $shop->taxzone_id,
211                     'currency'              => $::instance_conf->get_currency_id,
212                     'payment_id'            => $payment_id,
213                   );
214     $customer = SL::DB::Customer->new(%address);
215
216     $customer->save;
217     my $snumbers = "customernumber_" . $customer->customernumber;
218     SL::DB::History->new(
219                       trans_id    => $customer->id,
220                       snumbers    => $snumbers,
221                       employee_id => SL::DB::Manager::Employee->current->id,
222                       addition    => 'SAVED',
223                       what_done   => 'Shopimport',
224                     )->save();
225
226   }elsif(scalar(@{$customer_proposals}) == 1){
227     # check if the proposal is the right customer, could be different names under the same address. Depends on how first- and familyname is handled. Here is for customername = companyname or customername = "firstname familyname"
228     $customer = SL::DB::Manager::Customer->find_by( id       => $customer_proposals->[0]->id,
229                                                     name     => $name,
230                                                     email    => $self->billing_email,
231                                                     street   => $self->billing_street,
232                                                     zipcode  => $self->billing_zipcode,
233                                                     city     => $self->billing_city,
234                                                     obsolete => 'F',
235                                                   );
236   }
237
238   return $customer;
239 }
240
241 sub compare_to {
242   my ($self, $other) = @_;
243
244   return  1 if  $self->transfer_date && !$other->transfer_date;
245   return -1 if !$self->transfer_date &&  $other->transfer_date;
246
247   my $result = 0;
248   $result    = $self->transfer_date <=> $other->transfer_date if $self->transfer_date;
249   return $result || ($self->id <=> $other->id);
250 }
251
252 sub has_differing_delivery_address {
253   my ($self) = @_;
254   ($self->billing_firstname // '') ne ($self->delivery_firstname // '') ||
255   ($self->billing_lastname  // '') ne ($self->delivery_lastname  // '') ||
256   ($self->billing_city      // '') ne ($self->delivery_city      // '') ||
257   ($self->billing_street    // '') ne ($self->delivery_street    // '')
258 }
259
260 sub delivery_fullname {
261   ($_[0]->delivery_firstname // '') . " " . ($_[0]->delivery_lastname // '')
262 }
263
264 1;
265
266 __END__
267
268 =pod
269
270 =encoding utf-8
271
272 =head1 NAME
273
274 SL::DB::ShopOrder - Model for the 'shop_orders' table
275
276 =head1 SYNOPSIS
277
278 This is a standard Rose::DB::Object based model and can be used as one.
279
280 =head1 METHODS
281
282 =over 4
283
284 =item C<convert_to_sales_order>
285
286 =item C<check_for_existing_customers>
287
288 Inexact search for possible matches with existing customers in the database.
289
290 Returns all found customers as an arrayref of SL::DB::Customer objects.
291
292 =item C<get_customer>
293
294 returns only one customer from the check_for_existing_customers if the return from it is 0 or 1 customer.
295
296 When it is 0 get customer creates a new customer object of the shop order billing data and returns it
297
298 =item C<compare_to>
299
300 =back
301
302 =head1 TODO
303
304 some variables like payments could be better implemented. Transaction description is hardcoded
305
306 =head1 AUTHORS
307
308 Werner Hahn E<lt>wh@futureworldsearch.netE<gt>
309
310 G. Richardson E<lt>grichardson@kivitendo-premium.deE<gt>
311
312 =cut