Pflichtenheft: Zugriff auf nicht vorhandenes »visible_item« verhindern
[kivitendo-erp.git] / SL / DB / PurchaseInvoice.pm
1 package SL::DB::PurchaseInvoice;
2
3 use strict;
4
5 use Carp;
6
7 use SL::DB::MetaSetup::PurchaseInvoice;
8 use SL::DB::Manager::PurchaseInvoice;
9 use SL::DB::Helper::AttrHTML;
10 use SL::DB::Helper::AttrSorted;
11 use SL::DB::Helper::LinkedRecords;
12 use SL::DB::Helper::Payment qw(:ALL);
13 use SL::Locale::String qw(t8);
14 use Rose::DB::Object::Helpers qw(has_loaded_related forget_related);
15
16 # The calculator hasn't been adjusted for purchase invoices yet.
17 # use SL::DB::Helper::PriceTaxCalculator;
18
19 __PACKAGE__->meta->add_relationship(
20   invoiceitems   => {
21     type         => 'one to many',
22     class        => 'SL::DB::InvoiceItem',
23     column_map   => { id => 'trans_id' },
24     manager_args => { with_objects => [ 'part' ] }
25   },
26   sepa_export_items => {
27     type            => 'one to many',
28     class           => 'SL::DB::SepaExportItem',
29     column_map      => { id => 'ap_id' },
30     manager_args    => { with_objects => [ 'sepa_export' ] }
31   },
32   sepa_exports      => {
33     type            => 'many to many',
34     map_class       => 'SL::DB::SepaExportItem',
35     map_from        => 'ap',
36     map_to          => 'sepa_export',
37   },
38   custom_shipto     => {
39     type            => 'one to one',
40     class           => 'SL::DB::Shipto',
41     column_map      => { id => 'trans_id' },
42     query_args      => [ module => 'AP' ],
43   },
44   transactions   => {
45     type         => 'one to many',
46     class        => 'SL::DB::AccTransaction',
47     column_map   => { id => 'trans_id' },
48     manager_args => { with_objects => [ 'chart' ],
49                       sort_by      => 'acc_trans_id ASC' }
50   },
51 );
52
53 __PACKAGE__->meta->initialize;
54
55 __PACKAGE__->attr_html('notes');
56 __PACKAGE__->attr_sorted('items');
57
58 sub items { goto &invoiceitems; }
59 sub add_items { goto &add_invoiceitems; }
60 sub record_number { goto &invnumber; };
61
62 sub is_sales {
63   # For compatibility with Order, DeliveryOrder
64   croak 'not an accessor' if @_ > 1;
65   return 0;
66 }
67
68 sub date {
69   goto &transdate;
70 }
71
72 sub reqdate {
73   goto &duedate;
74 }
75
76 sub customervendor {
77   goto &vendor;
78 }
79
80 sub abbreviation {
81   my $self = shift;
82
83   return t8('AP Transaction (abbreviation)') if !$self->invoice && !$self->storno;
84   return t8('AP Transaction (abbreviation)') . '(' . t8('Storno (one letter abbreviation)') . ')' if !$self->invoice && $self->storno;
85   return t8('Invoice (one letter abbreviation)'). '(' . t8('Storno (one letter abbreviation)') . ')' if $self->storno;
86   return t8('Invoice (one letter abbreviation)');
87
88 };
89
90 sub link {
91   my ($self) = @_;
92
93   my $html;
94   $html   = SL::Presenter->get->purchase_invoice($self, display => 'inline') if $self->invoice;
95   $html   = SL::Presenter->get->ap_transaction($self, display => 'inline') if !$self->invoice;
96
97   return $html;
98 }
99
100 sub invoice_type {
101   my ($self) = @_;
102
103   return 'ap_transaction' if !$self->invoice;
104   return 'purchase_invoice';
105 }
106
107 sub displayable_type {
108   my ($self) = @_;
109
110   return t8('AP Transaction')    if $self->invoice_type eq 'ap_transaction';
111   return t8('Purchase Invoice');
112 }
113
114 sub displayable_name {
115   join ' ', grep $_, map $_[0]->$_, qw(displayable_type record_number);
116 };
117
118 sub create_ap_row {
119   my ($self, %params) = @_;
120   # needs chart as param
121   # to be called after adding all AP_amount rows
122
123   # only allow this method for ap invoices (Kreditorenbuchung)
124   die if $self->invoice and not $self->vendor_id;
125
126   my $acc_trans = [];
127   my $chart = $params{chart} || SL::DB::Manager::Chart->find_by(id => $::instance_conf->get_ap_chart_id);
128   die "illegal chart in create_ap_row" unless $chart;
129
130   die "receivables chart must have link 'AP'" . Dumper($chart) unless $chart->link eq 'AP';
131
132   # hardcoded entry for no tax, tax_id and taxkey should be 0
133   my $tax = SL::DB::Manager::Tax->find_by(id => 0, taxkey => 0) || die "Can't find tax with id 0 and taxkey 0";
134
135   my $sign = $self->vendor_id ? 1 : -1;
136   my $acc = SL::DB::AccTransaction->new(
137     amount     => $self->amount * $sign,
138     chart_id   => $params{chart}->id,
139     chart_link => $params{chart}->link,
140     transdate  => $self->transdate,
141     taxkey     => $tax->taxkey,
142     tax_id     => $tax->id,
143   );
144   $self->add_transactions( $acc );
145   push( @$acc_trans, $acc );
146   return $acc_trans;
147 };
148
149 sub add_ap_amount_row {
150   my ($self, %params ) = @_;
151
152   # only allow this method for ap invoices (Kreditorenbuchung)
153   die "not an ap invoice" if $self->invoice and not $self->vendor_id;
154
155   die "add_ap_amount_row needs a chart object as chart param" unless $params{chart} && $params{chart}->isa('SL::DB::Chart');
156   die unless $params{chart}->link =~ /AP_amount/;
157
158   my $acc_trans = [];
159
160   my $roundplaces = 2;
161   my ($netamount,$taxamount);
162
163   $netamount = $params{amount} * 1;
164   my $tax = SL::DB::Manager::Tax->find_by(id => $params{tax_id}) || die "Can't find tax with id " . $params{tax_id};
165
166   if ( $tax and $tax->rate != 0 ) {
167     ($netamount, $taxamount) = Form->calculate_tax($params{amount}, $tax->rate, $self->taxincluded, $roundplaces);
168   };
169   next unless $netamount; # netamount mustn't be zero
170
171   my $sign = $self->vendor_id ? -1 : 1;
172   my $acc = SL::DB::AccTransaction->new(
173     amount     => $netamount * $sign,
174     chart_id   => $params{chart}->id,
175     chart_link => $params{chart}->link,
176     transdate  => $self->transdate,
177     taxkey     => $tax->taxkey,
178     tax_id     => $tax->id,
179     project_id => $params{project_id},
180   );
181
182   $self->add_transactions( $acc );
183   push( @$acc_trans, $acc );
184
185   if ( $taxamount ) {
186      my $acc = SL::DB::AccTransaction->new(
187        amount     => $taxamount * $sign,
188        chart_id   => $tax->chart_id,
189        chart_link => $tax->chart->link,
190        transdate  => $self->transdate,
191        taxkey     => $tax->taxkey,
192        tax_id     => $tax->id,
193        project_id => $params{project_id},
194      );
195      $self->add_transactions( $acc );
196      push( @$acc_trans, $acc );
197   };
198   return $acc_trans;
199 };
200
201 1;