path => 'remote_path',
);
+my $VENDOR_KLUDGE_MAP = {
+ INVOIC => {
+ amount_billed_is_per_unit => [1699342]
+ },
+ ORDRSP => {
+ }
+};
+
__PACKAGE__->register_method(
method => 'retrieve',
}
+# Return hash with a key for every kludge that should apply for this
+# msg_type (INVOIC,ORDRSP) and this vendor SAN.
+sub get_kludges {
+ my ($class, $msg_type, $vendor_san) = @_;
+
+ my @kludges;
+ while (my ($kludge, $vendors) = each %{$VENDOR_KLUDGE_MAP->{$msg_type}}) {
+ push @kludges, $kludge if grep { $_ eq $vendor_san } @$vendors;
+ }
+
+ return map { $_ => 1 } @kludges;
+}
+
# create_acq_invoice_from_edi() does what it sounds like it does for INVOIC
# messages. For similar operation on ORDRSP messages, see the guts of
# process_jedi().
my $log_prefix = "create_acq_invoice_from_edi(..., <acq.edi_message #" .
$message->id . ">): ";
+ my %msg_kludges;
+ if ($msg_data->{vendor_san}) {
+ %msg_kludges = $class->get_kludges('INVOIC', $msg_data->{vendor_san});
+ } else {
+ $logger->warn($log_prefix . "no vendor_san field!");
+ }
+
my $eg_inv = Fieldmapper::acq::invoice->new;
+ # Some troubleshooting aids. Yeah we should have made appropriate links
+ # for this in the schema, but this is better than nothing. Probably
+ # *don't* try to i18n this.
+ $eg_inv->note("Generated from acq.edi_message #" . $message->id . ".");
+ if (%msg_kludges) {
+ $eg_inv->note(
+ $eg_inv->note .
+ " Vendor kludges: " . join(", ", keys(%msg_kludges)) . "."
+ );
+ }
+
$eg_inv->provider($provider);
$eg_inv->shipper($provider); # XXX Do we really have a meaningful way to
# distinguish provider and shipper?
# and $lineitem->{gross_unit_price}
my $lineitem_price = $lineitem->{amount_billed};
+ $lineitem_price *= $quantity if $msg_kludges{amount_billed_is_per_unit};
+
# if the top-level PO value is unset, get it from the first LI
$message->purchase_order($li->purchase_order)
unless $message->purchase_order;
push @eg_inv_cancel_lis,
{lineitem => $li, quantity => $quantity}
if $li->cancel_reason;
+
+ # The EDIReader class does detect certain per-lineitem taxes, but
+ # we'll ignore them for now, as the only sample invoices I've yet seen
+ # containing them also had a final cumulative tax at the end.
}
my @eg_inv_items;
my %charge_type_map = (
'TX' => ['TAX', 'Tax from electronic invoice'],
'CA' => ['PRO', 'Cataloging services'],
- 'DL' => ['SHP', 'Delivery']
- );
+ 'DL' => ['SHP', 'Delivery'],
+ 'GST' => ['TAX', 'Goods and services tax']
+ ); # XXX i18n, somehow
- for my $charge (@{$msg_data->{misc_charges}}) {
+ for my $charge (@{$msg_data->{misc_charges}}, @{$msg_data->{taxes}}) {
my $eg_inv_item = Fieldmapper::acq::invoice_item->new;
- my $amount = $charge->{charge_amount};
+ my $amount = $charge->{amount};
if (!$amount) {
$logger->warn($log_prefix . "charge with no amount");
next;
}
- my $map = $charge_type_map{$charge->{charge_type}};
+ my $map = $charge_type_map{$charge->{type}};
if (!$map) {
$map = [
'PRO',
- 'Unknown charge type ' . $charge->{charge_type}
+ 'Unknown charge type ' . $charge->{type}
];
}
$eg_inv_item->inv_item_type($$map[0]);
- $eg_inv_item->note($$map[1]);
+ $eg_inv_item->title($$map[1]); # title is user-visible; note isn't.
$eg_inv_item->cost_billed($amount);
+ $eg_inv_item->amount_paid($amount);
push @eg_inv_items, $eg_inv_item;
}
my $NEW_MSG_RE = '^UNH'; # starts a new message
my $NEW_LIN_RE = '^LIN'; # starts a new line item
+my $END_ALL_LIN = '^UNS'; # no more lineitems after this
my %edi_fields = (
message_type => qr/^UNH\+[A-z0-9]+\+(\S{6})/,
);
my %edi_charge_fields = (
- charge_type => qr/^ALC\+C\++([^\+]+)/,
- charge_amount => qr/^MOA\+(?:8|131|304):([^:]+)/
+ type => qr/^ALC\+C\++([^\+]+)/,
+ amount => qr/^MOA\+(?:8|131|304):([^:]+)/
+);
+
+# This may need to be liberalized later, but it works for the only example I
+# have so far.
+my %edi_tax_fields = (
+ type => qr/^TAX\+7\+([^\+]+)/,
+ amount => qr/^MOA\+124:([^:]+)/
);
sub new {
# - starting a new message
if (/$NEW_MSG_RE/) {
- $msg = {lineitems => [], misc_charges => []};
+ $msg = {lineitems => [], misc_charges => [], taxes => []};
push(@msgs, $msg);
}
# - starting a new misc. charge
- if (/$edi_charge_fields{charge_type}/) {
+ if (/$edi_charge_fields{type}/) {
$msg->{_current_charge} = {};
push (@{$msg->{misc_charges}}, $msg->{_current_charge});
}
if /$edi_charge_fields{$field}/;
}
}
+
+ # - starting a new tax charge. Taxes wind up on current lineitem if
+ # any, otherwise in the top-level taxes array
+
+ if (/$edi_tax_fields{type}/) {
+ $msg->{_current_tax} = {};
+ if ($msg->{_current_li}) {
+ $msg->{_current_li}{tax} = $msg->{_current_tax}
+ } else {
+ push (@{$msg->{taxes}}, $msg->{_current_tax});
+ }
+ }
+
+ # - extract tax field
+
+ if (my $tax = $msg->{_current_tax}) {
+ for my $field (keys %edi_tax_fields) {
+ ($tax->{$field}) = $_ =~ /$edi_tax_fields{$field}/
+ if /$edi_tax_fields{$field}/;
+ }
+ }
+
+ # This helps avoid associating taxes and charges at the end of the
+ # message with the final lineitem inapporiately.
+ if (/$END_ALL_LIN/) {
+ # remove the state-maintenance keys
+ foreach (grep /^_/, keys %$msg) {
+ delete $msg->{$_};
+ }
+ }
}
# remove the state-maintenance keys