X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/blobdiff_plain/ff159a4d47b9a2d10744dcfc23da2c63605c8a32..eeb5375ee7727c956cc357cc8f90b19d1bfe80b9:/SL/DB/DeliveryOrder.pm diff --git a/SL/DB/DeliveryOrder.pm b/SL/DB/DeliveryOrder.pm index 45d658876..f95f9a10e 100644 --- a/SL/DB/DeliveryOrder.pm +++ b/SL/DB/DeliveryOrder.pm @@ -4,7 +4,7 @@ use strict; use Carp; -use Rose::DB::Object::Helpers (); +use Rose::DB::Object::Helpers qw(as_tree strip); use SL::DB::MetaSetup::DeliveryOrder; use SL::DB::Manager::DeliveryOrder; @@ -12,14 +12,16 @@ use SL::DB::Helper::AttrHTML; use SL::DB::Helper::AttrSorted; use SL::DB::Helper::FlattenToForm; use SL::DB::Helper::LinkedRecords; +use SL::DB::Helper::TypeDataProxy; use SL::DB::Helper::TransNumberGenerator; - -use SL::DB::Part; -use SL::DB::Unit; +use SL::DB::Helper::RecordLink qw(RECORD_ID RECORD_TYPE_REF); use SL::DB::DeliveryOrder::TypeData qw(:types); +use SL::DB::Order::TypeData qw(:types); +use SL::DB::Reclamation::TypeData qw(:types); use SL::Helper::Number qw(_format_total _round_total); +use SL::Helper::ShippedQty; use List::Util qw(first); use List::MoreUtils qw(any pairwise); @@ -44,6 +46,8 @@ __PACKAGE__->attr_html('notes'); __PACKAGE__->attr_sorted('items'); __PACKAGE__->before_save('_before_save_set_donumber'); +__PACKAGE__->after_save('_after_save_link_records'); +__PACKAGE__->after_save('_mark_orders_if_delivered'); # hooks @@ -55,6 +59,30 @@ sub _before_save_set_donumber { return 1; } +sub _after_save_link_records { + my ($self) = @_; + + my @allowed_record_sources = qw(SL::DB::Reclamation SL::DB::Order); + my @allowed_item_sources = qw(SL::DB::ReclamationItem SL::DB::OrderItem); + + SL::DB::Helper::RecordLink::link_records( + $self, + \@allowed_record_sources, + \@allowed_item_sources, + ); +} + +sub _mark_orders_if_delivered { + my ($self) = @_; + my $orders = $self->linked_records(from => 'Order'); + SL::Helper::ShippedQty->new->calculate($orders)->write_to_objects; + foreach my $order (@$orders) { + next if $order->is_sales != $self->is_sales; + $order->update_attributes(delivered => $order->{delivered}); + } + return 1; +} + # methods sub items { goto &orderitems; } @@ -75,11 +103,11 @@ sub sales_order { ], ); - return first { $_->is_type('sales_order') } @{ $orders }; + return first { $_->is_type(SALES_ORDER_TYPE()) } @{ $orders }; } sub type { - goto &order_type; + goto &record_type; } sub is_type { @@ -87,12 +115,8 @@ sub is_type { } sub displayable_type { - my $type = shift->type; - - return $::locale->text('Sales Delivery Order') if $type eq 'sales_delivery_order'; - return $::locale->text('Purchase Delivery Order') if $type eq 'purchase_delivery_order'; - - die 'invalid type'; + my ($self) = @_; + return $self->type_data->text('type'); } sub displayable_name { @@ -115,6 +139,22 @@ sub number { goto &donumber; } +sub preceding_purchase_order_confirmations { + my ($self) = @_; + + my @lrs = (); + if ($self->id) { + @lrs = grep { $_->record_type eq PURCHASE_ORDER_CONFIRMATION_TYPE() } @{$self->linked_records(from => 'SL::DB::Order')}; + } else { + if ('SL::DB::Order' eq $self->{RECORD_TYPE_REF()}) { + my $order = SL::DB::Order->load_cached($self->{RECORD_ID()}); + push @lrs, $order if $order->record_type eq PURCHASE_ORDER_CONFIRMATION_TYPE(); + } + } + + return \@lrs; +} + sub _clone_orderitem_cvar { my ($cvar) = @_; @@ -124,68 +164,151 @@ sub _clone_orderitem_cvar { return $cloned; } +sub convert_to_reclamation { + my ($self, %params) = @_; + + $params{destination_type} = $self->is_sales ? SALES_RECLAMATION_TYPE() + : PURCHASE_RECLAMATION_TYPE(); + + my $reclamation = SL::DB::Reclamation->new_from($self, %params); + + return $reclamation; +} + sub new_from { my ($class, $source, %params) = @_; - croak("Unsupported source object type '" . ref($source) . "'") unless ref($source) eq 'SL::DB::Order'; + my %allowed_sources = map { $_ => 1 } qw( + SL::DB::Reclamation + SL::DB::Order + SL::DB::DeliveryOrder + ); + unless( $allowed_sources{ref $source} ) { + croak("Unsupported source object type '" . ref($source) . "'"); + } - my ($item_parent_id_column, $item_parent_column); + my %record_args = ( + donumber => undef, + employee => SL::DB::Manager::Employee->current, + closed => 0, + delivered => 0, + record_type => $params{destination_type}, + transdate => DateTime->today_local, + ); - if (ref($source) eq 'SL::DB::Order') { - $item_parent_id_column = 'trans_id'; - $item_parent_column = 'order'; + if ( ref($source) eq 'SL::DB::Order' ) { + map{ ( $record_args{$_} = $source->$_ ) } # {{{ for vim folds + qw( + billing_address_id + cp_id + currency_id + cusordnumber + customer_id + delivery_term_id + department_id + globalproject_id + intnotes + language_id + notes + payment_id + reqdate + salesman_id + shippingpoint + shipvia + taxincluded + taxzone_id + transaction_description + vendor_confirmation_number + vendor_id + ); + if ($source->record_type eq PURCHASE_ORDER_CONFIRMATION_TYPE()) { + $record_args{ordnumber} = join ' ', map { $_->ordnumber } @{$source->preceding_purchase_orders()}; + } else { + $record_args{ordnumber} = $source->ordnumber; + } + # }}} for vim folds + } elsif ( ref($source) eq 'SL::DB::Reclamation' ) { + map{ ( $record_args{$_} = $source->$_ ) } # {{{ for vim folds + qw( + billing_address_id + currency_id + customer_id + delivery_term_id + department_id + globalproject_id + intnotes + language_id + notes + payment_id + reqdate + salesman_id + shippingpoint + shipvia + taxincluded + taxzone_id + transaction_description + vendor_id + ); + $record_args{cp_id} = $source->contact_id; + $record_args{cusordnumber} = $source->cv_record_number; + $record_args{is_sales} = $source->is_sales; + # }}} for vim folds + } elsif ( ref($source) eq 'SL::DB::DeliveryOrder' ) { + map{ ( $record_args{$_} = $source->$_ ) } # {{{ for vim folds + qw( + billing_address_id + cp_id + currency_id + cusordnumber + customer_id + delivery_term_id + department_id + donumber + globalproject_id + intnotes + language_id + notes + ordnumber + oreqnumber + payment_id + reqdate + salesman_id + shippingpoint + shipto_id + shipvia + taxincluded + taxzone_id + transdate + transaction_description + vendor_confirmation_number + vendor_id + ); + # }}} for vim folds } - my %args = ( map({ ( $_ => $source->$_ ) } qw(cp_id currency_id customer_id cusordnumber delivery_term_id department_id employee_id globalproject_id intnotes language_id notes - ordnumber payment_id reqdate salesman_id shippingpoint shipvia taxincluded taxzone_id transaction_description vendor_id billing_address_id - )), - closed => 0, - delivered => 0, - order_type => $params{type}, - transdate => DateTime->today_local, - ); - # Custom shipto addresses (the ones specific to the sales/purchase # record and not to the customer/vendor) are only linked from # shipto → delivery_orders. Meaning delivery_orders.shipto_id # will not be filled in that case. if (!$source->shipto_id && $source->id) { - $args{custom_shipto} = $source->custom_shipto->clone($class) if $source->can('custom_shipto') && $source->custom_shipto; - + $record_args{custom_shipto} = $source->custom_shipto->clone($class) if $source->can('custom_shipto') && $source->custom_shipto; } else { - $args{shipto_id} = $source->shipto_id; + $record_args{shipto_id} = $source->shipto_id; } # infer type from legacy fields if not given - $args{order_type} //= $source->customer_id ? 'sales_delivery_order' - : $source->vendor_id ? 'purchase_delivery_order' - : $source->is_sales ? 'sales_delivery_order' - : croak "need some way to set delivery order type from source"; + $record_args{record_type} //= $source->customer_id ? SALES_DELIVERY_ORDER_TYPE() + : $source->vendor_id ? PURCHASE_DELIVERY_ORDER_TYPE() + : $source->is_sales ? SALES_DELIVERY_ORDER_TYPE() + : croak "need some way to set delivery order type from source"; - my $delivery_order = $class->new(%args); + my $delivery_order = $class->new(%record_args); $delivery_order->assign_attributes(%{ $params{attributes} }) if $params{attributes}; - my $items = delete($params{items}) || $source->items_sorted; - my %item_parents; - - # do not copy items when converting to supplier delivery order - my @items = $delivery_order->is_type(SUPPLIER_DELIVERY_ORDER_TYPE) ? () : map { - my $source_item = $_; - my $source_item_id = $_->$item_parent_id_column; - my @custom_variables = map { _clone_orderitem_cvar($_) } @{ $source_item->custom_variables }; - - $item_parents{$source_item_id} ||= $source_item->$item_parent_column; - my $item_parent = $item_parents{$source_item_id}; - - my $current_do_item = SL::DB::DeliveryOrderItem->new(map({ ( $_ => $source_item->$_ ) } - qw(base_qty cusordnumber description discount lastcost longdescription marge_price_factor parts_id price_factor price_factor_id - project_id qty reqdate sellprice serialnumber transdate unit active_discount_source active_price_source - )), - custom_variables => \@custom_variables, - ordnumber => ref($item_parent) eq 'SL::DB::Order' ? $item_parent->ordnumber : $source_item->ordnumber, - ); - $current_do_item->{"converted_from_orderitems_id"} = $_->{id} if ref($item_parent) eq 'SL::DB::Order'; - $current_do_item; - } @{ $items }; + + my $items = delete($params{items}) || $source->items_sorted; + my @items = ( $delivery_order->is_type(SUPPLIER_DELIVERY_ORDER_TYPE()) && ref($source) ne 'SL::DB::Reclamation' ) ? + () + : map { SL::DB::DeliveryOrderItem->new_from($_, %params) } @{ $items }; @items = grep { $params{item_filter}->($_) } @items if $params{item_filter}; @items = grep { $_->qty * 1 } @items if $params{skip_items_zero_qty}; @@ -193,11 +316,18 @@ sub new_from { $delivery_order->items(\@items); + unless ($params{no_linked_records}) { + $delivery_order->{ RECORD_ID() } = $source->id; + $delivery_order->{ RECORD_TYPE_REF() } = ref $source; + } + return $delivery_order; } sub new_from_time_recordings { my ($class, $sources, %params) = @_; + require SL::DB::Part; + require SL::DB::Unit; croak("Unsupported object type in sources") if any { ref($_) ne 'SL::DB::TimeRecording' } @$sources; croak("Cannot create delivery order from source records of different customers") if any { $_->customer_id != $sources->[0]->customer_id } @$sources; @@ -303,8 +433,7 @@ sub new_from_time_recordings { } else { my %args = ( - is_sales => 1, - order_type => 'sales_delivery_order', + record_type => SALES_DELIVERY_ORDER_TYPE, delivered => 0, customer_id => $sources->[0]->customer_id, taxzone_id => $sources->[0]->customer->taxzone_id, @@ -323,14 +452,14 @@ sub new_from_time_recordings { # legacy for compatibility # use type_data cusomtervendor and transfer direction instead sub is_sales { - if ($_[0]->order_type) { - return SL::DB::DeliveryOrder::TypeData::get3($_[0]->order_type, "properties", "is_customer"); + if ($_[0]->record_type) { + return SL::DB::DeliveryOrder::TypeData::get3($_[0]->record_type, "properties", "is_customer"); } return $_[0]{is_sales}; } sub customervendor { - SL::DB::DeliveryOrder::TypeData::get3($_[0]->order_type, "properties", "is_customer") ? $_[0]->customer : $_[0]->vendor; + SL::DB::DeliveryOrder::TypeData::get3($_[0]->record_type, "properties", "is_customer") ? $_[0]->customer : $_[0]->vendor; } sub convert_to_invoice { @@ -342,22 +471,6 @@ sub convert_to_invoice { if (!$self->db->with_transaction(sub { require SL::DB::Invoice; $invoice = SL::DB::Invoice->new_from($self, %params)->post || die; - $self->link_to_record($invoice); - # TODO extend link_to_record for items, otherwise long-term no d.r.y. - foreach my $item (@{ $invoice->items }) { - foreach (qw(delivery_order_items)) { # expand if needed (orderitems) - if ($item->{"converted_from_${_}_id"}) { - die unless $item->{id}; - RecordLinks->create_links('mode' => 'ids', - 'from_table' => $_, - 'from_ids' => $item->{"converted_from_${_}_id"}, - 'to_table' => 'invoice', - 'to_id' => $item->{id}, - ) || die; - delete $item->{"converted_from_${_}_id"}; - } - } - } $self->update_attributes(closed => 1); 1; })) { @@ -376,6 +489,10 @@ sub digest { $self->date->to_kivitendo; } +sub type_data { + SL::DB::Helper::TypeDataProxy->new(ref $_[0], $_[0]->type); +} + 1; __END__