From 9a59cc9d51350d7eefb25697cd0d99188f686ea5 Mon Sep 17 00:00:00 2001 From: levinse Date: Wed, 8 Jun 2011 05:38:55 +0000 Subject: [PATCH] DID inventory/import / bulk DID orders - phase 2, RT12754 --- FS/FS/Schema.pm | 1 + FS/FS/did_order.pm | 91 ++++++++++++++++++++++++++++++-- FS/FS/did_order_item.pm | 5 +- FS/FS/phone_avail.pm | 16 +++--- bin/import-did-inventory | 8 ++- httemplate/browse/did_order.html | 81 +++++++++++++++++++++------- httemplate/edit/did_order.html | 8 ++- httemplate/edit/process/did_order.html | 27 ++++++---- httemplate/elements/did_order_item.html | 12 +++++ httemplate/misc/did_order_confirmed.html | 81 +++++++++++++++++++--------- httemplate/misc/did_order_provision.html | 82 +++++++++++++++------------- 11 files changed, 308 insertions(+), 104 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 9d03ce39d..eebcfecd2 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -3184,6 +3184,7 @@ sub tables_hashref { 'ratecenternum', 'int', 'NULL', '', '', '', 'state', 'char', 'NULL', 2, '', '', 'quantity', 'int', '', '', '', '', + 'custnum', 'int', 'NULL', '', '', '', ], 'primary_key' => 'orderitemnum', 'unique' => [], diff --git a/FS/FS/did_order.pm b/FS/FS/did_order.pm index c1b34c345..2404ad817 100644 --- a/FS/FS/did_order.pm +++ b/FS/FS/did_order.pm @@ -126,6 +126,59 @@ sub delete { $dbh->commit or die $dbh->errstr if $oldAutoCommit; } +=item merge SOURCE_ORDER + +Merges the DID order given by SOURCE_ORDER into THIS order. + +The following fields from the source order are transferred, only if they aren't +set in this order: +-vendor order # +-submitted +-confirmed +-customer + +DID order items are transferred into this order. + +The source order is deleted. + +The operation fails if: +-either order has a received time; or +-the DID vendors do not match between the orders + +=cut + +sub merge { + my $self = shift; + my $src = shift; + return "invalid source order" unless $src; + + return "DIDs received for either order" + if $src->received || $self->received; + + return "DID vendors do not match" + if $src->vendornum != $self->vendornum; + + my @move_if_unset = qw( vendor_order_id submitted confirmed custnum ); + foreach my $f ( @move_if_unset ) { + $self->$f($src->$f) if !$self->$f; + } + + my $error = ''; + my @did_order_items = qsearch('did_order_item', { 'ordernum' => $src->ordernum }); + foreach my $did_order_item ( @did_order_items ) { + $did_order_item->ordernum($self->ordernum); + $error = $did_order_item->replace; + return $error if $error; + } + + $error = $src->delete; + return $error if !$error; + + $error = $self->replace; + return $error if !$error; + + ''; +} =item replace OLD_RECORD @@ -176,16 +229,48 @@ sub did_order_item { =item cust_main -Returns the cust_main (see L), if any, associated with this bulk DID order. +Returns all cust_main (see L), if any, associated with this +bulk DID order. =cut sub cust_main { my $self = shift; - return '' unless $self->custnum; - qsearchs('cust_main', { 'custnum' => $self->custnum } ); + my @did_order_item = $self->did_order_item; + my @custnums; + push @custnums, $self->custnum if $self->custnum; + foreach my $did_order_item ( @did_order_item ) { + push @custnums, $did_order_item->custnum if $did_order_item->custnum; + } + my @cust_main; + foreach my $custnum ( @custnums ) { + push @cust_main, qsearchs('cust_main', { 'custnum' => $custnum } ); + } + @cust_main; +} + + +=item has_stock + +Returns true if and only if the order has any stock order items. + +=cut + +sub has_stock { + my $self = shift; + my $items_with_custnum = 0; + my @did_order_item = $self->did_order_item; + foreach my $did_order_item ( @did_order_item ) { + $items_with_custnum++ if $did_order_item->custnum; + } + + return 0 if ($items_with_custnum == scalar(@did_order_item) + && $items_with_custnum != 0 && !$self->custnum) + || $self->custnum; + 1; } + =item provisioned Returns the provisioned DIDs, if any, as phone_avail (see L) objects. diff --git a/FS/FS/did_order_item.pm b/FS/FS/did_order_item.pm index c2d657ad8..235e00eb7 100644 --- a/FS/FS/did_order_item.pm +++ b/FS/FS/did_order_item.pm @@ -49,6 +49,8 @@ primary key =item quantity +=item custnum - foreign key to cust_main table, optional + =back =head1 METHODS @@ -117,6 +119,7 @@ sub check { || $self->ut_foreign_keyn('ratecenternum', 'rate_center', 'ratecenternum') || $self->ut_textn('state') || $self->ut_number('quantity') + || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum') ; return $error if $error; @@ -127,7 +130,7 @@ sub check { =head1 SEE ALSO -L, schema.html from the base documentation. +L, , schema.html from the base documentation. =cut diff --git a/FS/FS/phone_avail.pm b/FS/FS/phone_avail.pm index b726d3497..44421ff57 100644 --- a/FS/FS/phone_avail.pm +++ b/FS/FS/phone_avail.pm @@ -226,14 +226,14 @@ sub process_batch_import { 'postinsert_callback' => sub { my $record = shift; if($record->ordernum) { - my $did_order = qsearchs('did_order', - { 'ordernum' => $record->ordernum } ); - if($did_order && !$did_order->received) { - $did_order->received(time); - $did_order->confirmed(parse_datetime($record->confirmed)); - $did_order->vendor_order_id($record->vendor_order_id); - $did_order->replace; - } + my $did_order = qsearchs('did_order', + { 'ordernum' => $record->ordernum } ); + if($did_order && !$did_order->received) { + $did_order->received(time); + $did_order->confirmed(parse_datetime($record->confirmed)); + $did_order->vendor_order_id($record->vendor_order_id); + $did_order->replace; + } } }, }; diff --git a/bin/import-did-inventory b/bin/import-did-inventory index 11d68a775..612217863 100644 --- a/bin/import-did-inventory +++ b/bin/import-did-inventory @@ -362,8 +362,12 @@ sub order { && $o->received == $received); fatal("customer mismatch for vendor order #$vendor_order_id") unless ( ($o->custnum && $cust{'custnum'} - && $o->custnum == $cust{'custnum'}) - || (!$o->custnum && !exists($cust{'custnum'})) ); + && ($o->custnum == $cust{'custnum'} + || $vendor_order_id eq '293745') + ) + || + (!$o->custnum && !exists($cust{'custnum'})) + ); } else { $o = new FS::did_order{ vendornum => $did_vendor_id, vendor_order_id => $vendor_order_id, diff --git a/httemplate/browse/did_order.html b/httemplate/browse/did_order.html index 3da8cb1ba..61b37d8d9 100644 --- a/httemplate/browse/did_order.html +++ b/httemplate/browse/did_order.html @@ -4,10 +4,10 @@ 'name' => 'bulk DID orders', 'disableable' => 0, 'query' => $query, - 'count_query' => 'SELECT COUNT(*) FROM did_order', # XXX: this is wrong!? + 'count_query' => 'SELECT COUNT(*) FROM did_order', # XXX: this is wrong! 'header' => [ '#', 'Vendor',' Vendor Order #', 'Submitted', 'Confirmed', 'Customer', - 'Received', 'Provision', 'Cancel', + 'Received', 'Provision', 'Cancel', 'Merge Into', ], 'fields' => [ sub { my $did_order = shift; @@ -32,9 +32,19 @@ }, sub { # Customer my $did_order = shift; - my $cust_main = $did_order->cust_main; - return "Stock" unless $cust_main; - "".$cust_main->name.""; + my @cust_main = $did_order->cust_main; + my $has_stock = $did_order->has_stock; + if (scalar(@cust_main) == 1 && !$has_stock) { + my $cust_main = $cust_main[0]; + return "".$cust_main->name."" + } + elsif(scalar(@cust_main) == 0 && $has_stock) { + return 'Stock'; + } + elsif(scalar(@cust_main) > 0 && !$has_stock) { + return 'Multiple'; + } + 'Multiple/Stock'; }, sub { # Received my $did_order = shift; @@ -50,11 +60,11 @@ my @provisioned = $did_order->provisioned; return '' unless $did_order->received - && $did_order->custnum + && scalar($did_order->cust_main) && !scalar(@provisioned); include( '/elements/popup_link.html', { 'action' => "${p}misc/did_order_provision.html?ordernum=".$did_order->ordernum, - 'label' => 'Provision All DIDs', + 'label' => 'Provision DIDs', 'actionlabel' => 'Bulk DID order - DID provisioning', 'width' => 520, 'height' => 300, @@ -68,6 +78,11 @@ . $did_order->ordernum . qq!', 'Cancel this order (#! . $did_order->ordernum . qq!)?')">Cancel! }, + sub { # Merge + my $did_order = shift; + return '' unless !$did_order->received; + &$merge_select($did_order->ordernum); + }, ], 'links' => [ [ $p.'edit/did_order.html?', 'ordernum' ], @@ -78,6 +93,12 @@ if (confirm(msg)) window.location.href = href; } + + function mergeconfirm(select,source_ordernum) { + var target_ordernum = select.options[select.selectedIndex].value; + areyousure("'.$p.'misc/did_order_confirmed.html?action=merge;ordernum="+source_ordernum+";target_ordernum="+target_ordernum, + "Merge order #" + source_ordernum + " into order #" + target_ordernum + "?"); + } ', ) @@ -90,27 +111,26 @@ die "access denied" my $conf = new FS::Conf; my $date_format = $conf->config('date_format') || '%m/%d/%Y'; -my $display_date = sub { - my $date = shift; - return '' unless $date; - time2str($date_format, $date); -}; - my $html_init = qq!Add a bulk DID order

!; - my $query = { 'table' => 'did_order', 'hashref' => {}, 'addl_from' => 'left join did_vendor using (vendornum) ', 'order_by' => 'ORDER BY ordernum', }; -$query->{'hashref'}->{'custnum'} = $1 if $cgi->param('custnum') =~ /^(\d+)$/; +if ( $cgi->param('custnum') =~ /^(\d+)$/ ) { + $query->{'extra_sql'} .= " where (custnum = $1 or $1 in (select distinct " + . " custnum from did_order_item where " + . " did_order_item.ordernum = did_order.ordernum ) )"; +} if ( $cgi->param('custrcvdunprov') ) { $query->{'hashref'}->{'received'} = { 'op' => '>', 'value' => '0', }; - $query->{'hashref'}->{'custnum'} = { 'op' => '>', 'value' => '0', }; + $query->{'hashref'}->{'svcnum'} = ''; $query->{'addl_from'} .= ' left join phone_avail using (ordernum) '; - $query->{'extra_sql'} .= ' and svcnum is null '; + $query->{'extra_sql'} .= " and (custnum > 0 or 0 < (select count(1) from " + . " did_order_item where did_order_item.custnum > 0 and " + . " did_order_item.ordernum = did_order.ordernum) )"; $html_init .= qq!Browse all DID orders!; } else { @@ -118,7 +138,32 @@ else { Browse all non-stock orders with received unprovisioned DIDs !; } - $html_init .= "

"; +my @merge_targets = qsearch({ + 'table' => 'did_order', + 'hashref' => { 'received' => '' }, + 'order_by' => 'ORDER BY ordernum', + }); + +my $merge_select = sub { + my $source_ordernum = shift; + my $merge_select = ''; + if ( @merge_targets ) { + $merge_select = ""; + } + $merge_select; +}; + +my $display_date = sub { + my $date = shift; + return '' unless $date; + time2str($date_format, $date); +}; + diff --git a/httemplate/edit/did_order.html b/httemplate/edit/did_order.html index 5837d438c..6912117de 100644 --- a/httemplate/edit/did_order.html +++ b/httemplate/edit/did_order.html @@ -12,6 +12,12 @@ { field => 'confirmed', type => 'hidden', }, + { field => 'received', + type => 'hidden', + }, + { field => 'submitted', + type => 'hidden', + }, { field => 'stock_or_customer', type => 'radio', options => [ 'Stock', 'Customer', ], @@ -104,12 +110,12 @@ my $javascript = < { 'table' => 'did_order_item', 'fields' => [ qw( msanum npa latanum ratecenternum state - quantity ) ], + quantity custnum ) ], }, ) %> @@ -25,14 +25,23 @@ my $value_callback = sub { my @params = $cgi->param; foreach my $param ( @params ) { - next unless $param =~ /^(orderitemnum[0-9]+)_rc_new$/; - my $prefix = $1; - my $value = $cgi->param($param); - next unless $value =~ /^[A-Za-z0-9\- ]+$/; - my $rc = new FS::rate_center({ description => $value }); - my $error = $rc->insert; - die "error inserting new rate center: $error" if $error; - $cgi->param("${prefix}_ratecenternum",$rc->ratecenternum); + if ( $param =~ /^(orderitemnum[0-9]+)_rc_new$/ ) { + my $prefix = $1; + my $value = $cgi->param($param); + if( $value =~ /^[A-Za-z0-9\- ]+$/ ) { + my $rc = new FS::rate_center({ description => $value }); + my $error = $rc->insert; + die "error inserting new rate center: $error" if $error; + $cgi->param("${prefix}_ratecenternum",$rc->ratecenternum); + } + } + elsif ( $param =~ /^(orderitemnum[0-9]+)_custnum$/ ) { + my $prefix = $1; + my $value = $cgi->param($param); + if ( $value =~ /^\d+$/ ) { + $cgi->param("custnum",''); + } + } } diff --git a/httemplate/elements/did_order_item.html b/httemplate/elements/did_order_item.html index b582683b7..d1f90f338 100644 --- a/httemplate/elements/did_order_item.html +++ b/httemplate/elements/did_order_item.html @@ -98,6 +98,18 @@
State +% $value = $item->get('custnum'); + " + <% $onchange %> + > +
Customer # + + diff --git a/httemplate/misc/did_order_confirmed.html b/httemplate/misc/did_order_confirmed.html index c0c4795a6..3505a1e1d 100644 --- a/httemplate/misc/did_order_confirmed.html +++ b/httemplate/misc/did_order_confirmed.html @@ -1,17 +1,19 @@ -<% include('/elements/header-popup.html', $success_msg ) %> +<% include("/elements/header$popup.html", $header ) %> +<% include('/elements/error.html') %> +% unless ( $cgi->param('error') ) { +% } <%init> die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Import'); -$cgi->param('action') =~ /^(confirm|cancel|provision)$/ or die 'illegal action'; my $action = $1; -my $success_msg = ''; -my $error = ''; +my $header = ''; +my $popup = ''; my $js = 'window.top.location.reload();'; $cgi->param('ordernum') =~ /^(\d+)$/ or die 'illegal ordernum'; @@ -22,9 +24,10 @@ my $did_order = qsearchs( { } ); die "No order $ordernum" unless $did_order; +my $action = $cgi->param('action'); if ( $action eq 'confirm' ) { my $confirmed = ''; - my $sucess_msg = 'DID order confirmed'; + $header = 'DID order confirmed'; $confirmed = parse_datetime($cgi->param('confirmed')) if $cgi->param('confirmed') && $cgi->param('confirmed') !~ /^\d+$/; $confirmed = $1 @@ -34,34 +37,62 @@ if ( $action eq 'confirm' ) { $did_order->confirmed($confirmed); $did_order->vendor_order_id($cgi->param('vendor_order_id')); - $error = $did_order->replace; - if ( $error ) { - $cgi->param('error', $error); - print $cgi->redirect(popurl(1). "did_order_confirm.html?". $cgi->query_string ); - } + $cgi->param('error',$did_order->replace); + print $cgi->redirect(popurl(1). "did_order_confirm.html?". $cgi->query_string ) + if $cgi->param('error'); + $popup = '-popup'; } elsif ( $action eq 'cancel' ) { - my $sucess_msg = 'DID order cancelled'; - $error = $did_order->delete; + $header = 'DID order cancelled'; + $cgi->param('error',$did_order->delete); $js = "window.location.href = '${p}browse/did_order.html'"; } elsif ( $action eq 'provision' ) { - my $sucess_msg = 'DID order provisioned'; - $cgi->param('pkgnum_svcpart') =~ /^(\d+)_(\d+)$/ or die 'illegal pkgnum_svcpart'; - my $pkgnum = $1; - my $svcpart = $2; + $header = 'DID order provisioned'; my @dids = qsearch( 'phone_avail', { ordernum => $ordernum } ); die "no DIDs on order" unless scalar(@dids); - foreach my $did ( @dids ) { - my $svc_phone = new FS::svc_phone({ - pkgnum => $pkgnum, - svcpart => $svcpart, - countrycode => 1, - phonenum => $did->npa.$did->nxx.$did->station, - }); - $error = $svc_phone->insert; - last if $error; + my @params = $cgi->param; + foreach my $param ( @params ) { + next unless $param =~ /^custnum(\d+)_pkgnum_svcpart$/; + my $custnum = $1; + my $value = $cgi->param($param); + next unless $value =~ /^(\d+)_(\d+)$/; + my $pkgnum = $1; + my $svcpart = $2; + if ( $did_order->custnum && $did_order->custnum == $custnum ) { + # this is our old per-order custnum case + + foreach my $did ( @dids ) { + my $svc_phone = new FS::svc_phone({ + pkgnum => $pkgnum, + svcpart => $svcpart, + countrycode => 1, + phonenum => $did->npa.$did->nxx.$did->station, + }); + $cgi->param('error',$svc_phone->insert); + last if $cgi->param('error'); + } + } + # XXX: now what do we do for order items? Not clear how to match a DID in an order item } + $popup = '-popup'; +} +elsif ( $action eq 'merge' ) { + $header = 'DID order merged'; + $cgi->param('target_ordernum') =~ /^(\d+)$/ or die 'illegal target_ordernum'; + my $target_ordernum = $1; + my $target_did_order = qsearchs( { + 'table' => 'did_order', + 'hashref' => { 'ordernum' => $target_ordernum }, + } ); + die "No order $target_ordernum" unless $target_did_order; + $cgi->param('error',$target_did_order->merge($did_order)); + $js = "window.location.href = '${p}browse/did_order.html'"; +} +else { + die "illegal action"; } +$header = 'Error' if $cgi->param('error'); + diff --git a/httemplate/misc/did_order_provision.html b/httemplate/misc/did_order_provision.html index 8241121a8..1df9444ab 100644 --- a/httemplate/misc/did_order_provision.html +++ b/httemplate/misc/did_order_provision.html @@ -5,27 +5,33 @@
+ + - - - + + + + + + +% my $anyavail = 0; +% foreach my $cust_main ( @cust_main ) { +% my($cust_pkg_phone,$cust_pkg_label,$svc_label) = possible_pkgs($cust_main); +% my $avail = keys(%$cust_pkg_phone); +% $anyavail = 1 if $avail; - - - -% if ( $avail ) { - - - -% } +% } +% if ( $anyavail ) { + +% }
Order #<% $ordernum %>
Order # <% $ordernum %>
CustomerPackage/Service
Customer <% $cust_main->name %>
Package/Service % if ( !$avail ) { - No packages exist for this customer having at least <% scalar(@dids) %> - unprovisioned DIDs, as required for this order. + No suitable packages exist for this customer. % } else { - +% foreach my $pkgnum ( keys %$cust_pkg_phone ) { +% my @svcpart = @{$cust_pkg_phone->{$pkgnum}}; % foreach my $svcpart ( @svcpart ) { % } % } @@ -33,11 +39,10 @@ % }
<%init> @@ -53,34 +58,37 @@ my $did_order = qsearchs( { } ); die "No order $ordernum" unless $did_order; -die "Order is not in received status and/or DIDs not assigned to a customer" - unless $did_order->received && $did_order->custnum; +my @cust_main = $did_order->cust_main; -my $cust_main = qsearchs('cust_main', { custnum => $did_order->custnum } ); -die "invalid customer" unless $cust_main; - -my @pkgs = $cust_main->ncancelled_pkgs; -die "no packages" unless scalar(@pkgs); +die "Order is not in received status and/or DIDs not assigned to any customers" + unless $did_order->received && scalar(@cust_main); my @dids = qsearch( 'phone_avail', { ordernum => $ordernum } ); die "no DIDs on order" unless scalar(@dids); -my (%cust_pkg_phone, %cust_pkg_label, %svc_label ); +sub possible_pkgs { + my $cust_main = shift; + + my (%cust_pkg_phone, %cust_pkg_label, %svc_label); + + my @pkgs = $cust_main->ncancelled_pkgs; + return (\%cust_pkg_phone,\%cust_pkg_label,\%svc_label) unless scalar(@pkgs); -foreach my $pkg ( @pkgs ) { - my @avail_part_svc = $pkg->available_part_svc; - my @svcpart; - foreach my $avail_part_svc ( @avail_part_svc ) { - if ($avail_part_svc->svcdb eq 'svc_phone' - && $avail_part_svc->num_avail >= scalar(@dids)) { - push @svcpart, $avail_part_svc->svcpart; - $svc_label{$avail_part_svc->svcpart} = $avail_part_svc->svc; + foreach my $pkg ( @pkgs ) { + my @avail_part_svc = $pkg->available_part_svc; + my @svcpart; + foreach my $avail_part_svc ( @avail_part_svc ) { + if ($avail_part_svc->svcdb eq 'svc_phone' + && $avail_part_svc->num_avail >= scalar(@dids)) { + push @svcpart, $avail_part_svc->svcpart; + $svc_label{$avail_part_svc->svcpart} = $avail_part_svc->svc; + } } + $cust_pkg_phone{$pkg->pkgnum} = \@svcpart if scalar(@svcpart); + $cust_pkg_label{$pkg->pkgnum} = $pkg->part_pkg->pkg; } - $cust_pkg_phone{$pkg->pkgnum} = \@svcpart if scalar(@svcpart); - $cust_pkg_label{$pkg->pkgnum} = $pkg->part_pkg->pkg; + + (\%cust_pkg_phone,\%cust_pkg_label,\%svc_label); } -my $avail = keys(%cust_pkg_phone); - -- 2.11.0