Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 34099460

Von Tamino Steinert vor etwa 2 Jahren hinzugefügt

  • ID 340994601f6aa0cb973af4f62b7a81677abd38d8
  • Vorgänger f95d6823
  • Nachfolger 985c6455

Reclamation: sql-script for data tables and rose objects added

Also created all needed functions in SL/DB/Reclamation.pm,
SL/DB/ReclamationItem.pm and SL/DB/ReclamationReason.pm

Unterschiede anzeigen:

SL/DB/Helper/ALL.pm
use SL::DB::ProjectStatus;
use SL::DB::ProjectType;
use SL::DB::PurchaseInvoice;
use SL::DB::Reclamation;
use SL::DB::ReclamationItem;
use SL::DB::ReclamationReason;
use SL::DB::ReconciliationLink;
use SL::DB::RecordLink;
use SL::DB::RecordTemplate;
SL/DB/Helper/Mappings.pm
project_roles => 'project_role',
project_statuses => 'project_status',
project_types => 'project_type',
reclamations => 'Reclamation',
reclamation_items => 'ReclamationItem',
reclamation_reasons => 'ReclamationReason',
reconciliation_links => 'reconciliation_link',
record_links => 'record_link',
record_templates => 'record_template',
SL/DB/Manager/Reclamation.pm
package SL::DB::Manager::Reclamation;
use strict;
use parent qw(SL::DB::Helper::Manager);
use SL::DB::Helper::Paginated;
use SL::DB::Helper::Sorted;
use SL::DB::Helper::Filtered;
sub object_class { 'SL::DB::Reclamation' }
__PACKAGE__->make_manager_methods;
__PACKAGE__->add_filter_specs(
type => sub {
my ($key, $value, $prefix) = @_;
return __PACKAGE__->type_filter($value, $prefix);
},
# todo when is this used?
#all => sub {
# my ($key, $value, $prefix) = @_;
# return or => [ map { $prefix . $_ => $value } qw(record_number customer.name vendor.name transaction_description) ]
#}
);
sub type_filter {
my $class = shift;
my $type = lc(shift || '');
my $prefix = shift || '';
return (and => [ "!customer_id" => undef ]) if $type eq 'sales_reclamation';
return (and => [ "!vendor_id" => undef ]) if $type eq 'purchase_reclamation';
die "Unknown type $type";
}
sub _sort_spec {
return (
default => [ 'transdate', 1 ],
nulls => {
transaction_description => 'FIRST',
default => 'LAST',
},
columns => {
SIMPLE => 'ALL',
customer => 'lower(customer.name)',
vendor => 'lower(vendor.name)',
employee => 'lower(employee.name)',
globalprojectnumber => 'lower(globalproject.projectnumber)',
# Bug in Rose::DB::Object: the next should be
# "globalproject.project_type.description". This workaround will
# only work if no other table with "project_type" is visible in
# the current query
globalproject_type => 'lower(project_type.description)',
map { ( $_ => "lower(reclamations.$_)" ) }
qw(record_number cv_record_number shippingpoint shipvia notes intnotes
transaction_description
),
});
}
1;
SL/DB/Manager/ReclamationItem.pm
# This file has been auto-generated only because it didn't exist.
# Feel free to modify it at will; it will not be overwritten automatically.
package SL::DB::Manager::ReclamationItem;
use strict;
use parent qw(SL::DB::Helper::Manager);
sub object_class { 'SL::DB::ReclamationItem' }
__PACKAGE__->make_manager_methods;
1;
SL/DB/Manager/ReclamationReason.pm
package SL::DB::Manager::ReclamationReason;
use strict;
use parent qw(SL::DB::Helper::Manager);
use SL::DB::Helper::Paginated;
use SL::DB::Helper::Filtered;
use SL::DB::Helper::Sorted;
sub object_class { 'SL::DB::ReclamationReason' }
__PACKAGE__->make_manager_methods;
__PACKAGE__->add_filter_specs(
all => sub {
my ($key, $value, $prefix) = @_;
return or => [ map { $prefix . $_ => $value } qw(reclamation_reason) ]
},
);
sub _sort_spec {
return ( default => [ 'position', 1 ],
columns => { SIMPLE => 'ALL' });
}
1;
SL/DB/MetaSetup/Reclamation.pm
# This file has been auto-generated. Do not modify it; it will be overwritten
# by rose_auto_create_model.pl automatically.
package SL::DB::Reclamation;
use strict;
use parent qw(SL::DB::Object);
__PACKAGE__->meta->table('reclamations');
__PACKAGE__->meta->columns(
amount => { type => 'numeric', precision => 15, scale => 5 },
closed => { type => 'boolean', default => 'false', not_null => 1 },
contact_id => { type => 'integer' },
currency_id => { type => 'integer', not_null => 1 },
customer_id => { type => 'integer' },
cv_record_number => { type => 'text' },
delivered => { type => 'boolean', default => 'false', not_null => 1 },
delivery_term_id => { type => 'integer' },
department_id => { type => 'integer' },
employee_id => { type => 'integer', not_null => 1 },
exchangerate => { type => 'numeric', precision => 15, scale => 5 },
globalproject_id => { type => 'integer' },
id => { type => 'integer', not_null => 1, sequence => 'id' },
intnotes => { type => 'text' },
itime => { type => 'timestamp', default => 'now()' },
language_id => { type => 'integer' },
mtime => { type => 'timestamp' },
netamount => { type => 'numeric', precision => 15, scale => 5 },
notes => { type => 'text' },
payment_id => { type => 'integer' },
record_number => { type => 'text', not_null => 1 },
reqdate => { type => 'date' },
salesman_id => { type => 'integer' },
shippingpoint => { type => 'text' },
shipto_id => { type => 'integer' },
shipvia => { type => 'text' },
tax_point => { type => 'date' },
taxincluded => { type => 'boolean', not_null => 1 },
taxzone_id => { type => 'integer', not_null => 1 },
transaction_description => { type => 'text' },
transdate => { type => 'date', default => 'now()' },
vendor_id => { type => 'integer' },
);
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
__PACKAGE__->meta->allow_inline_column_values(1);
__PACKAGE__->meta->foreign_keys(
contact => {
class => 'SL::DB::Contact',
key_columns => { contact_id => 'cp_id' },
},
currency => {
class => 'SL::DB::Currency',
key_columns => { currency_id => 'id' },
},
customer => {
class => 'SL::DB::Customer',
key_columns => { customer_id => 'id' },
},
delivery_term => {
class => 'SL::DB::DeliveryTerm',
key_columns => { delivery_term_id => 'id' },
},
department => {
class => 'SL::DB::Department',
key_columns => { department_id => 'id' },
},
employee => {
class => 'SL::DB::Employee',
key_columns => { employee_id => 'id' },
},
globalproject => {
class => 'SL::DB::Project',
key_columns => { globalproject_id => 'id' },
},
language => {
class => 'SL::DB::Language',
key_columns => { language_id => 'id' },
},
payment => {
class => 'SL::DB::PaymentTerm',
key_columns => { payment_id => 'id' },
},
salesman => {
class => 'SL::DB::Employee',
key_columns => { salesman_id => 'id' },
},
shipto => {
class => 'SL::DB::Shipto',
key_columns => { shipto_id => 'shipto_id' },
},
taxzone => {
class => 'SL::DB::TaxZone',
key_columns => { taxzone_id => 'id' },
},
vendor => {
class => 'SL::DB::Vendor',
key_columns => { vendor_id => 'id' },
},
);
1;
;
SL/DB/MetaSetup/ReclamationItem.pm
# This file has been auto-generated. Do not modify it; it will be overwritten
# by rose_auto_create_model.pl automatically.
package SL::DB::ReclamationItem;
use strict;
use parent qw(SL::DB::Object);
__PACKAGE__->meta->table('reclamation_items');
__PACKAGE__->meta->columns(
active_discount_source => { type => 'text', default => '', not_null => 1 },
active_price_source => { type => 'text', default => '', not_null => 1 },
base_qty => { type => 'float', precision => 4, scale => 4 },
description => { type => 'text' },
discount => { type => 'float', precision => 4, scale => 4 },
id => { type => 'serial', not_null => 1 },
itime => { type => 'timestamp', default => 'now()' },
lastcost => { type => 'numeric', precision => 15, scale => 5 },
longdescription => { type => 'text' },
mtime => { type => 'timestamp' },
parts_id => { type => 'integer', not_null => 1 },
position => { type => 'integer', not_null => 1 },
price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
price_factor_id => { type => 'integer' },
pricegroup_id => { type => 'integer' },
project_id => { type => 'integer' },
qty => { type => 'float', precision => 4, scale => 4 },
reason_description_ext => { type => 'text' },
reason_description_int => { type => 'text' },
reason_id => { type => 'integer', not_null => 1 },
reclamation_id => { type => 'integer', not_null => 1 },
reqdate => { type => 'date' },
sellprice => { type => 'numeric', precision => 15, scale => 5 },
serialnumber => { type => 'text' },
unit => { type => 'varchar', length => 20 },
);
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
__PACKAGE__->meta->allow_inline_column_values(1);
__PACKAGE__->meta->foreign_keys(
part => {
class => 'SL::DB::Part',
key_columns => { parts_id => 'id' },
},
price_factor_obj => {
class => 'SL::DB::PriceFactor',
key_columns => { price_factor_id => 'id' },
},
pricegroup => {
class => 'SL::DB::Pricegroup',
key_columns => { pricegroup_id => 'id' },
},
project => {
class => 'SL::DB::Project',
key_columns => { project_id => 'id' },
},
reason => {
class => 'SL::DB::ReclamationReason',
key_columns => { reason_id => 'id' },
},
reclamation => {
class => 'SL::DB::Reclamation',
key_columns => { reclamation_id => 'id' },
},
unit_obj => {
class => 'SL::DB::Unit',
key_columns => { unit => 'name' },
},
);
1;
;
SL/DB/MetaSetup/ReclamationReason.pm
# This file has been auto-generated. Do not modify it; it will be overwritten
# by rose_auto_create_model.pl automatically.
package SL::DB::ReclamationReason;
use strict;
use parent qw(SL::DB::Object);
__PACKAGE__->meta->table('reclamation_reasons');
__PACKAGE__->meta->columns(
description => { type => 'text', not_null => 1 },
id => { type => 'serial', not_null => 1 },
itime => { type => 'timestamp', default => 'now()' },
mtime => { type => 'timestamp' },
name => { type => 'text', not_null => 1 },
position => { type => 'integer', not_null => 1 },
valid_for_purchase => { type => 'boolean', default => 'false', not_null => 1 },
valid_for_sales => { type => 'boolean', default => 'false', not_null => 1 },
);
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
__PACKAGE__->meta->allow_inline_column_values(1);
1;
;
SL/DB/Reclamation.pm
package SL::DB::Reclamation;
use utf8;
use strict;
use Carp;
use DateTime;
use List::Util qw(max sum0);
use List::MoreUtils qw(any);
use SL::DB::MetaSetup::Reclamation;
use SL::DB::Manager::Reclamation;
use SL::DB::Helper::Attr;
use SL::DB::Helper::AttrHTML;
use SL::DB::Helper::AttrSorted;
use SL::DB::Helper::FlattenToForm;
use SL::DB::Helper::LinkedRecords;
use SL::DB::Helper::PriceTaxCalculator;
use SL::DB::Helper::PriceUpdater;
use SL::DB::Helper::TransNumberGenerator;
use SL::Locale::String qw(t8);
use SL::RecordLinks;
use Rose::DB::Object::Helpers qw(as_tree);
__PACKAGE__->meta->add_relationship(
reclamation_items => {
type => 'one to many',
class => 'SL::DB::ReclamationItem',
column_map => { id => 'reclamation_id' },
manager_args => {
with_objects => [ 'part', 'reason' ]
}
},
custom_shipto => {
type => 'one to one',
class => 'SL::DB::Shipto',
column_map => { id => 'trans_id' },
query_args => [ module => 'Reclamation' ],
},
exchangerate_obj => {
type => 'one to one',
class => 'SL::DB::Exchangerate',
column_map => { currency_id => 'currency_id', transdate => 'transdate' },
},
);
SL::DB::Helper::Attr::make(__PACKAGE__, daily_exchangerate => 'numeric');
__PACKAGE__->meta->initialize;
__PACKAGE__->attr_html('notes');
__PACKAGE__->attr_sorted('items');
__PACKAGE__->before_save('_before_save_set_record_number');
__PACKAGE__->before_save('_before_save_remove_empty_custom_shipto');
__PACKAGE__->before_save('_before_save_set_custom_shipto_module');
# hooks
sub _before_save_set_record_number {
my ($self) = @_;
$self->create_trans_number if !$self->record_number;
return 1;
}
sub _before_save_remove_empty_custom_shipto {
my ($self) = @_;
$self->custom_shipto(undef) if $self->custom_shipto && $self->custom_shipto->is_empty;
return 1;
}
sub _before_save_set_custom_shipto_module {
my ($self) = @_;
$self->custom_shipto->module('Reclamation') if $self->custom_shipto;
return 1;
}
# methods
sub items { goto &reclamation_items; }
sub add_items { goto &add_reclamation_items; }
sub record_items { goto &reclamation_items; }
sub type {
my ($self) = @_;
return 'sales_reclamation' if $self->customer_id;
return 'purchase_reclamation' if $self->vendor_id;
return;
}
sub is_type {
my ($self, $type) = @_;
return $self->type eq $type;
}
sub effective_tax_point {
my ($self) = @_;
return $self->tax_point || $self->reqdate || $self->transdate;
}
sub displayable_type {
my $type = shift->type;
return $::locale->text('Sales Reclamation') if $type eq 'sales_reclamation';
return $::locale->text('Purchase Reclamation') if $type eq 'purchase_reclamation';
die 'invalid type';
}
sub displayable_name {
join ' ', grep $_, map $_[0]->$_, qw(displayable_type record_number);
};
sub is_sales {
croak 'not an accessor' if @_ > 1;
return !!shift->customer_id;
}
sub daily_exchangerate {
my ($self, $val) = @_;
return 1 if $self->currency_id == $::instance_conf->get_currency_id;
my $rate = (any { $self->is_type($_) } qw(sales_reclamation)) ? 'buy'
: (any { $self->is_type($_) } qw(purchase_reclamation)) ? 'sell'
: undef;
return if !$rate;
if (defined $val) {
croak t8('exchange rate has to be positive') if $val <= 0;
if (!$self->exchangerate_obj) {
$self->exchangerate_obj(SL::DB::Exchangerate->new(
currency_id => $self->currency_id,
transdate => $self->transdate,
$rate => $val,
));
} elsif (!defined $self->exchangerate_obj->$rate) {
$self->exchangerate_obj->$rate($val);
} else {
croak t8('exchange rate already exists, no update allowed');
}
}
return $self->exchangerate_obj->$rate if $self->exchangerate_obj;
}
sub taxes {
my ($self) = @_;
# add taxes to recalmation
my %pat = $self->calculate_prices_and_taxes();
my @taxes;
foreach my $tax_id (keys %{ $pat{taxes_by_tax_id} }) {
my $netamount = sum0 map { $pat{amounts}->{$_}->{amount} } grep { $pat{amounts}->{$_}->{tax_id} == $tax_id } keys %{ $pat{amounts} };
push(@taxes, { amount => $pat{taxes_by_tax_id}->{$tax_id},
netamount => $netamount,
tax => SL::DB::Tax->new(id => $tax_id)->load });
}
return \@taxes;
}
sub displayable_state {
my ($self) = @_;
return $self->closed ? $::locale->text('closed') : $::locale->text('open');
}
sub valid_reclamation_reasons {
my ($self) = @_;
my $valid_for_type = ($self->type =~ m{sales} ? 'valid_for_sales' : 'valid_for_purchase');
return SL::DB::Manager::ReclamationReason->get_all_sorted(
where => [ $valid_for_type => 1 ]);
}
#TODO(Werner): überprüfen ob alle Felder richtig gestetzt werden
sub new_from {
my ($class, $source, %params) = @_;
my %allowed_sources = map { $_ => 1 } qw(
SL::DB::Reclamation
);
unless( $allowed_sources{ref $source} ) {
croak("Unsupported source object type '" . ref($source) . "'");
}
croak("A destination type must be given as parameter") unless $params{destination_type};
my $destination_type = delete $params{destination_type};
my @from_tos = (
#Reclamation
{ from => 'sales_reclamation', to => 'sales_reclamation', abbr => 'srsr', },
{ from => 'purchase_reclamation', to => 'purchase_reclamation', abbr => 'prpr', },
{ from => 'sales_reclamation', to => 'purchase_reclamation', abbr => 'srpr', },
{ from => 'purchase_reclamation', to => 'sales_reclamation', abbr => 'prsr', },
);
my $from_to = (grep { $_->{from} eq $source->type && $_->{to} eq $destination_type} @from_tos)[0];
if (!$from_to) {
croak("Cannot convert from '" . $source->type . "' to '" . $destination_type . "'");
}
my $is_abbr_any = sub {
any { $from_to->{abbr} eq $_ } @_;
};
my %record_args = (
record_number => undef,
employee => SL::DB::Manager::Employee->current,
closed => 0,
delivered => 0,
transdate => DateTime->today_local,
reqdate => DateTime->today_local->next_workday(),
);
if ( $is_abbr_any->(qw(srsr prpr srpr prsr)) ) { #Reclamation
map { $record_args{$_} = $source->$_ } # {{{ for vim folds
qw(
amount
contact_id
currency_id
customer_id
cv_record_number
delivery_term_id
department_id
exchangerate
globalproject_id
intnotes
language_id
netamount
notes
payment_id
salesman_id
shippingpoint
shipvia
tax_point
taxincluded
taxzone_id
transaction_description
vendor_id
); # }}} for vim folds
}
if ( ($from_to->{from} =~ m{sales}) && ($from_to->{to} =~ m{purchase}) ) {
$record_args{customer_id} = undef;
$record_args{salesman_id} = undef;
$record_args{payment_id} = undef;
$record_args{delivery_term_id} = undef;
}
if ( ($from_to->{from} =~ m{purchase}) && ($from_to->{to} =~ m{sales}) ) {
$record_args{vendor_id} = undef;
$record_args{salesman_id} = undef;
$record_args{payment_id} = undef;
}
if ($source->can('shipto_id')) {
# Custom shipto addresses (the ones specific to the sales/purchase record and
# not to the customer/vendor) are only linked from shipto → record.
# Meaning record.shipto_id will not be filled in that case.
if (!$source->shipto_id && $source->id) {
$record_args{custom_shipto} = $source->custom_shipto->clone($class) if $source->can('custom_shipto') && $source->custom_shipto;
} elsif ($source->shipto_id) {
$record_args{shipto_id} = $source->shipto_id;
}
}
my $reclamation = $class->new(%record_args);
$reclamation->assign_attributes(%{ $params{attributes} }) if $params{attributes};
unless ($params{no_linked_records}) {
$reclamation->{"converted_from_record_type_ref"} = ref($source);
$reclamation->{"converted_from_record_id"} = $source->id;
};
my $items = delete($params{items}) || $source->items;
my @items = map { SL::DB::ReclamationItem->new_from($_, $from_to->{to}, no_linked_records => $params{no_linked_records}); } @{ $items };
@items = grep { $params{item_filter}->($_) } @items if $params{item_filter};
@items = grep { $_->qty * 1 } @items if $params{skip_items_zero_qty};
@items = grep { $_->qty >=0 } @items if $params{skip_items_negative_qty};
$reclamation->items(\@items);
return $reclamation;
}
sub customervendor {
my ($reclamation) = @_;
return $reclamation->is_sales ? $reclamation->customer : $reclamation->vendor;
}
sub date {
goto &transdate;
}
sub digest {
my ($self) = @_;
sprintf "%s %s %s (%s)",
$self->record_number,
$self->customervendor->name,
$self->amount_as_number,
$self->date->to_kivitendo;
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
SL::DB::Reclamation - reclamation Datenbank Objekt.
=head1 FUNCTIONS
=head2 C<type>
Returns one of the following string types:
=over 4
=item sales_reclamation
=item purchase_reclamation
=item sales_quotation
=item request_quotation
=back
=head2 C<is_type TYPE>
Returns true if the reclamation is of the given type.
=head2 C<daily_exchangerate $val>
Gets or sets the exchangerate object's value. This is the value from the
table C<exchangerate> depending on the reclamation's currency, the transdate and
if it is a sales or purchase reclamation.
The reclamation object (respectively the table C<oe>) has an own column
C<exchangerate> which can be get or set with the accessor C<exchangerate>.
The idea is to drop the legacy table C<exchangerate> in the future and to
give all relevant tables it's own C<exchangerate> column.
So, this method is here if you need to access the "legacy" exchangerate via
an reclamation object.
=over 4
=item C<$val>
(optional) If given, the exchangerate in the "legacy" table is set to this
value, depending on currency, transdate and sales or purchase.
=back
=head2 C<convert_to_delivery_order %params>
Creates a new delivery reclamation with C<$self> as the basis by calling
L<SL::DB::DeliveryReclamation::new_from>. That delivery reclamation is saved, and
C<$self> is linked to the new invoice via
L<SL::DB::RecordLink>. C<$self>'s C<delivered> attribute is set to
C<true>, and C<$self> is saved.
The arguments in C<%params> are passed to
L<SL::DB::DeliveryReclamation::new_from>.
Returns C<undef> on failure. Otherwise the new delivery reclamation will be
returned.
=head2 C<convert_to_invoice %params>
Creates a new invoice with C<$self> as the basis by calling
L<SL::DB::Invoice::new_from>. That invoice is posted, and C<$self> is
linked to the new invoice via L<SL::DB::RecordLink>. C<$self>'s
C<closed> attribute is set to C<true>, and C<$self> is saved.
The arguments in C<%params> are passed to L<SL::DB::Invoice::post>.
Returns the new invoice instance on success and C<undef> on
failure. The whole process is run inside a transaction. On failure
nothing is created or changed in the database.
At the moment only sales quotations and sales reclamations can be converted.
=head2 C<new_from $source, %params>
Creates a new C<SL::DB::Reclamation> instance and copies as much
information from C<$source> as possible. At the moment only records with the
same destination type as the source type and sales reclamations from
sales quotations and purchase reclamations from requests for quotations can be
created.
The C<transdate> field will be set to the current date.
The conversion copies the reclamation items as well.
Returns the new reclamation instance. The object returned is not
saved.
C<%params> can include the following options
(C<destination_type> is mandatory):
=over 4
=item C<destination_type>
(mandatory)
The type of the newly created object. Can be C<sales_quotation>,
C<sales_reclamation>, C<purchase_quotation> or C<purchase_reclamation> for now.
=item C<items>
An optional array reference of RDBO instances for the items to use. If
missing then the method C<items_sorted> will be called on
C<$source>. This option can be used to override the sorting, to
exclude certain positions or to add additional ones.
=item C<skip_items_negative_qty>
If trueish then items with a negative quantity are skipped. Items with
a quantity of 0 are not affected by this option.
=item C<skip_items_zero_qty>
If trueish then items with a quantity of 0 are skipped.
=item C<item_filter>
An optional code reference that is called for each item with the item
as its sole parameter. Items for which the code reference returns a
falsish value will be skipped.
=item C<attributes>
An optional hash reference. If it exists then it is passed to C<new>
allowing the caller to set certain attributes for the new delivery
reclamation.
=back
=head1 BUGS
Nothing here yet.
=head1 AUTHOR
Sven Schöling <s.schoeling@linet-services.de>
=cut
SL/DB/ReclamationItem.pm
package SL::DB::ReclamationItem;
use utf8;
use strict;
use List::MoreUtils qw(any);
use SL::DB::MetaSetup::ReclamationItem;
use SL::DB::Manager::ReclamationItem;
use SL::DB::ReclamationReason;
use SL::DB::Helper::ActsAsList;
use SL::DB::Helper::LinkedRecords;
use SL::DB::Helper::RecordItem;
use SL::DB::Helper::CustomVariables (
sub_module => 'reclamation_items',
cvars_alias => 1,
overloads => {
parts_id => {
class => 'SL::DB::Part',
module => 'IC',
}
},
);
use SL::Helper::ShippedQty;
__PACKAGE__->meta->initialize;
__PACKAGE__->configure_acts_as_list(group_by => [qw(reclamation_id)]);
sub shipped_qty {
my ($self, %params) = @_;
my $force = delete $params{force};
SL::Helper::ShippedQty->new(%params)->calculate($self)->write_to_objects if $force || !defined $self->{shipped_qty};
$self->{shipped_qty};
}
sub is_linked_to_record {
my ($self) = @_;
if(scalar(@{$self->linked_records}) || $self->{converted_from_record_item_type_ref}) {
return 1;
}
return 0;
}
#TODO(Werner): überprüfen ob alle Felder richtig gestetzt werden
sub new_from {
my ($class, $source, $parent_type, %params) = @_;
unless (any {ref($source) eq $_}
qw(
SL::DB::ReclamationItem
)
) {
croak("Unsupported source object type '" . ref($source) . "'");
}
my @custom_variables = map { _clone_cvar_for_reclamation_item($_) } @{ $source->custom_variables };
my %item_args;
if (ref($source) eq 'SL::DB::ReclamationItem') {
map { $item_args{$_} = $source->$_ } qw(
active_discount_source active_price_source base_qty description discount
lastcost longdescription parts_id position price_factor price_factor_id
pricegroup_id project_id qty reason_description_ext reason_description_int
reason_id reqdate sellprice serialnumber
unit
);
$item_args{custom_variables} = \@custom_variables;
}
my $item = $class->new(%item_args);
if ( $source->record->is_sales() && ($parent_type =~ m{sales}) ) {
$item->sellprice($source->lastcost);
$item->discount(0);
}
if ( !$source->record->is_sales() && ($parent_type =~ m{purchase}) ) {
$item->lastcost($source->sellprice);
}
$item->assign_attributes(%{ $params{attributes} }) if $params{attributes};
unless ($params{no_linked_records}) {
$item->{"converted_from_record_item_type_ref"} = ref($source);
$item->{"converted_from_record_item_id"} = $source->id;
}
return $item;
}
sub _clone_cvar_for_reclamation_item {
my ($cvar) = @_;
my $cloned = $_->clone_and_reset;
$cloned->sub_module('reclamation_items');
return $cloned;
}
sub customervendor {
my ($self) = @_;
return $self->reclamation->customervendor;
}
sub delivered_qty { goto &shipped_qty }
sub record { goto &reclamation }
sub record_id { goto &reclamation_id }
sub trans_id { goto &reclamation_id }
sub date { goto &reqdate }
1;
__END__
=pod
=head1 NAME
SL::DB::ReclamationItems: Rose model for reclamationitems
=head1 FUNCTIONS
=over 4
=item C<shipped_qty PARAMS>
Calculates the shipped qty for this reclamationitem (measured in the current unit)
and returns it.
Note that the shipped qty is expected not to change within the request and is
cached in C<shipped_qty> once calculated. If C<< force => 1 >> is passed, the
existibng cache is ignored.
Given parameters will be passed to L<SL::Helper::ShippedQty>, so you can force
the shipped/delivered distinction like this:
$_->shipped_qty(require_stock_out => 0);
Note however that calculating shipped_qty on individual Reclamationitems is generally
a bad idea. See L<SL::Helper::ShippedQty> for way to compute these all at once.
=item C<delivered_qty>
Alias for L</shipped_qty>.
=back
=head1 AUTHORS
G. Richardson E<lt>grichardson@kivitendo-premium.deE<gt>
=cut
SL/DB/ReclamationReason.pm
package SL::DB::ReclamationReason;
use strict;
use SL::DB::MetaSetup::ReclamationReason;
use SL::DB::Manager::ReclamationReason;
use SL::DB::Helper::ActsAsList
__PACKAGE__->meta->initialize;
sub validate {
my ($self) = @_;
my @errors;
if (!$self->name) {
push @errors, $::locale->text('The name is missing.');
}
if (!$self->description) {
push @errors, $::locale->text('The description is missing.');
}
return @errors;
}
1;
scripts/rose_auto_create_model.pl
ap => { payment_id => 'payment_terms', },
orderitems => { parts_id => 'part', trans_id => 'order', },
reclamation_items => { parts_id => 'part' },
delivery_order_items => { parts_id => 'part' },
invoice => { parts_id => 'part' },
follow_ups => { created_by => 'created_by_employee', },
sql/Pg-upgrade2/clean_up_record_links_before_delete_trigger.sql
-- @tag: clean_up_record_links_before_delete_trigger
-- @description: delete trigger for record_links clean up
-- @depends: release_3_5_7
CREATE OR REPLACE FUNCTION clean_up_record_links_before_delete() RETURNS trigger AS $$
BEGIN
DELETE FROM record_links
WHERE (from_table = TG_TABLE_NAME AND from_id = OLD.id)
OR (to_table = TG_TABLE_NAME AND to_id = OLD.id);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
sql/Pg-upgrade2/delete_cvars_on_trans_deletion_add_reclamation_items.sql
-- @tag: delete_cvars_on_trans_deletion_add_reclamation_items
-- @description: Add reclamation_items to trigger
-- @depends: delete_cvars_on_trans_deletion_add_shipto reclamations
CREATE OR REPLACE FUNCTION delete_custom_variables_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF (TG_TABLE_NAME IN ('orderitems', 'delivery_order_items', 'invoice', 'reclamation_items')) THEN
PERFORM delete_custom_variables_with_sub_module('IC', TG_TABLE_NAME, old.id);
END IF;
IF (TG_TABLE_NAME = 'parts') THEN
PERFORM delete_custom_variables_with_sub_module('IC', '', old.id);
END IF;
IF (TG_TABLE_NAME IN ('customer', 'vendor')) THEN
PERFORM delete_custom_variables_with_sub_module('CT', '', old.id);
END IF;
IF (TG_TABLE_NAME = 'contacts') THEN
PERFORM delete_custom_variables_with_sub_module('Contacts', '', old.cp_id);
END IF;
IF (TG_TABLE_NAME = 'project') THEN
PERFORM delete_custom_variables_with_sub_module('Projects', '', old.id);
END IF;
IF (TG_TABLE_NAME = 'shipto') THEN
PERFORM delete_custom_variables_with_sub_module('ShipTo', '', old.shipto_id);
END IF;
RETURN old;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER reclamation_items_delete_custom_variables_after_deletion
AFTER DELETE ON reclamation_items
FOR EACH ROW EXECUTE PROCEDURE delete_custom_variables_trigger();
sql/Pg-upgrade2/reclamations.sql
-- @tag: reclamations
-- @description: Add reclamations, reclamation_items and reclamation_reasons
-- @depends: release_3_5_7
-- @ignore: 0
CREATE TABLE reclamation_reasons (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
description TEXT NOT NULL,
position INTEGER NOT NULL,
itime TIMESTAMP without time zone DEFAULT now(),
mtime TIMESTAMP without time zone,
valid_for_sales BOOLEAN NOT NULL DEFAULT false,
valid_for_purchase BOOLEAN NOT NULL DEFAULT false
);
CREATE TRIGGER mtime_reclamation_reasons
BEFORE UPDATE ON reclamation_reasons
FOR EACH ROW EXECUTE PROCEDURE set_mtime();
CREATE TABLE reclamations (
--basic
id INTEGER NOT NULL DEFAULT nextval('id'),
record_number TEXT NOT NULL,
transdate DATE DEFAULT now(),
itime TIMESTAMP without time zone DEFAULT now(),
mtime TIMESTAMP without time zone,
delivered BOOLEAN NOT NULL DEFAULT false,
closed BOOLEAN NOT NULL DEFAULT false,
--header
employee_id INTEGER NOT NULL REFERENCES employee(id),
globalproject_id INTEGER REFERENCES project(id),
delivery_term_id INTEGER REFERENCES delivery_terms(id),
shipto_id INTEGER REFERENCES shipto(shipto_id),
department_id INTEGER REFERENCES department(id),
contact_id INTEGER REFERENCES contacts(cp_id),
shipvia TEXT,
transaction_description TEXT,
shippingpoint TEXT,
cv_record_number TEXT,
reqdate DATE,
--money/summery
amount NUMERIC(15,5),
netamount NUMERIC(15,5),
payment_id INTEGER REFERENCES payment_terms(id),
currency_id INTEGER NOT NULL REFERENCES currencies(id),
taxincluded BOOLEAN NOT NULL,
tax_point DATE,
exchangerate NUMERIC(15,5),
taxzone_id INTEGER NOT NULL REFERENCES tax_zones(id),
--other
notes TEXT,
intnotes TEXT,
language_id INTEGER REFERENCES language(id),
salesman_id INTEGER REFERENCES employee(id),
customer_id INTEGER REFERENCES customer(id),
vendor_id INTEGER REFERENCES vendor(id),
CONSTRAINT reclamations_customervendor_check CHECK (
(customer_id IS NOT NULL AND vendor_id IS NULL)
OR (vendor_id IS NOT NULL AND customer_id IS NULL)
),
PRIMARY KEY (id)
);
CREATE TRIGGER mtime_reclamations BEFORE UPDATE ON reclamations FOR EACH ROW EXECUTE PROCEDURE set_mtime();
ALTER TABLE defaults ADD COLUMN p_reclamation_record_number TEXT NOT NULL DEFAULT 0;
ALTER TABLE defaults ADD COLUMN s_reclamation_record_number TEXT NOT NULL DEFAULT 0;
CREATE TABLE reclamation_items (
--base
id SERIAL PRIMARY KEY,
reclamation_id INTEGER NOT NULL REFERENCES reclamations(id) ON DELETE CASCADE,
reason_id INTEGER NOT NULL REFERENCES reclamation_reasons(id),
reason_description_ext TEXT,
reason_description_int TEXT,
position INTEGER NOT NULL CHECK(position > 0),
itime TIMESTAMP without time zone DEFAULT now(),
mtime TIMESTAMP without time zone,
--header
project_id INTEGER REFERENCES project(id) ON DELETE SET NULL,
--part description
parts_id INTEGER NOT NULL REFERENCES parts(id),
description TEXT,
longdescription TEXT,
serialnumber TEXT,
base_qty REAL,
qty REAL,
unit character varying(20) REFERENCES units(name),
--money
sellprice NUMERIC(15,5),
lastcost NUMERIC(15,5),
discount REAL,
pricegroup_id INTEGER REFERENCES pricegroup(id),
price_factor_id INTEGER REFERENCES price_factors(id),
price_factor NUMERIC(15,5) DEFAULT 1,
active_price_source TEXT NOT NULL DEFAULT ''::text,
active_discount_source TEXT NOT NULL DEFAULT ''::text,
--other
reqdate DATE
);
CREATE TRIGGER mtime_reclamation_items BEFORE UPDATE ON reclamation_items FOR EACH ROW EXECUTE PROCEDURE set_mtime();
sql/Pg-upgrade2/record_links_delete_triggers__reclamations__reclamation_items.sql
-- @tag: record_links_delete_triggers__reclamations__reclamation_items
-- @description: delete corresponding record_links if reclamation or reclamation_item is deleted
-- @depends: clean_up_record_links_before_delete_trigger reclamations
CREATE TRIGGER before_delete_reclamation_items_clean_up_record_linkes_trigger
BEFORE DELETE ON reclamation_items FOR EACH ROW EXECUTE
PROCEDURE clean_up_record_links_before_delete();
CREATE TRIGGER before_delete_reclamations_clean_up_record_linkes_trigger
BEFORE DELETE ON reclamations FOR EACH ROW EXECUTE
PROCEDURE clean_up_record_links_before_delete();

Auch abrufbar als: Unified diff