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