site/templates/custom/payment_type_email.include
site/templates/email/base.tmpl
site/templates/email/email.css
+site/templates/email/ordershipped.tmpl
site/templates/error.tmpl
site/templates/error_base.tmpl
site/templates/extras.txt
freight_tracking varchar(255) not null default '',
+ stage varchar(20) not null default '',
+
primary key (id),
index order_cchash(ccNumberHash),
index order_userId(userId, orderDate)
use Carp 'confess';
@ISA = qw(BSE::DB);
-our $VERSION = "1.001";
+our $VERSION = "1.002";
use vars qw($VERSION $MAX_CONNECTION_AGE);
SQL
Orders => 'select * from orders',
- getOrderByPkey => 'select * from orders where id = ?',
+ #getOrderByPkey => 'select * from orders where id = ?',
getOrderItemByOrderId => 'select * from order_item where orderId = ?',
- addOrder => 'insert orders values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
- replaceOrder => 'replace orders values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ #addOrder => 'insert orders values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ #replaceOrder => 'replace orders values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
addOrderItem => 'insert order_item values(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
replaceOrderItem => 'replace order_item values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
- getOrderByUserId => 'select * from orders where userId = ?',
+ #getOrderByUserId => 'select * from orders where userId = ?',
deleteOrdersItems => 'delete from order_item where orderId = ?',
getOrderItemByProductId => 'select * from order_item where productId = ?',
@ISA = qw/Squirrel::Row/;
use Carp 'confess';
-our $VERSION = "1.002";
+our $VERSION = "1.004";
sub columns {
return qw/id
ccStatus2 ccTranId complete delivOrganization billOrganization
delivStreet2 billStreet2 purchase_order shipping_method
shipping_name shipping_trace
- paypal_token paypal_tran_id freight_tracking/;
+ paypal_token paypal_tran_id freight_tracking stage/;
+}
+
+sub table {
+ return "orders";
}
sub defaults {
require Digest::MD5;
return
(
+ billFirstName => "",
+ billLastName => "",
+ billStreet => "",
+ billSuburb => "",
+ billState => "",
+ billPostCode => "",
+ billCountry => "",
total => 0,
wholesaleTotal => 0,
gst => 0,
shipping_method => '',
shipping_name => '',
shipping_trace => undef,
+ paypal_token => "",
+ paypal_tran_id => "",
freight_tracking => "",
+ stage => "incomplete",
);
}
sub siteuser {
my ($self) = @_;
+ if ($self->siteuser_id) {
+ require SiteUsers;
+ my $user = SiteUsers->getByPkey($self->siteuser_id);
+ $user and return $user;
+ }
+
$self->{userId} or return;
require SiteUsers;
}
}
+=item stage
+
+Return the order stage.
+
+If the stage is empty, guess from the order flags.
+
+=cut
+
+sub stage {
+ my ($self) = @_;
+
+ if ($self->{stage} ne "") {
+ return $self->{stage};
+ }
+
+ if (!$self->complete) {
+ return "incomplete";
+ }
+ elsif ($self->filled) {
+ return "shipped";
+ }
+ else {
+ return "unprocessed";
+ }
+}
+
+sub stage_description {
+ my ($self, $lang) = @_;
+
+ return BSE::TB::Orders->stage_label($self->stage, $lang);
+}
+
+=item mail_recipient
+
+Return a value suitable for BSE::ComposeMail's to parameter.
+
+=cut
+
+sub mail_recipient {
+ my ($self) = @_;
+
+ my $user = $self->siteuser;
+
+ if ($user && $user->email eq $self->emailAddress) {
+ return $user;
+ }
+
+ return $self->emailAddress;
+}
+
+sub send_shipped_email {
+ my ($self) = @_;
+
+ my $to = $self->mail_recipient;
+ require BSE::ComposeMail;
+ my $mailer = BSE::ComposeMail->new(cfg => BSE::Cfg->single);
+ require BSE::Util::Tags;
+ require BSE::Util::Iterate;
+ my $it = BSE::Util::Iterate::Objects->new;
+ my %acts =
+ (
+ BSE::Util::Tags->mail_tags(),
+ order => [ \&BSE::Util::Tags::tag_object_plain, $self ],
+ $it->make
+ (
+ single => "orderitem",
+ plural => "orderitems",
+ code => [ items => $self ],
+ ),
+ );
+
+ $mailer->send
+ (
+ to => $to,
+ subject => "Your order has shipped",
+ template => "email/ordershipped",
+ acts => \%acts,
+ log_msg => "Notify customer order has shipped",
+ log_object => $self,
+ log_component => "shopadmin:orders:saveorder",
+ );
+}
+
+sub new_stage {
+ my ($self, $who, $stage, $stage_note) = @_;
+
+ unless ($stage ne $self->stage
+ || defined $stage_note && $stage_note =~ /\S/) {
+ return;
+ }
+
+ my $old_stage = $self->stage;
+ my $msg = "Set to stage '$stage'";
+ if (defined $stage_note && $stage_note =~ /\S/) {
+ $msg .= ": $stage_note";
+ }
+ require BSE::TB::AuditLog;
+ BSE::TB::AuditLog->log
+ (
+ component => "shopadmin:orders:saveorder",
+ object => $self,
+ msg => $msg,
+ level => "info",
+ actor => $who || "U"
+ );
+
+ if ($stage ne $old_stage) {
+ $self->set_stage($stage);
+ if ($stage eq "shipped") {
+ $self->send_shipped_email();
+ $self->set_filled(1);
+ }
+ else {
+ $self->set_filled(0);
+ }
+ }
+}
+
1;
@ISA = qw(Squirrel::Table);
use BSE::TB::Order;
-our $VERSION = "1.002";
+our $VERSION = "1.003";
sub rowClass {
return 'BSE::TB::Order';
return @ship;
}
+=item stages()
+
+Return the possible values for the order stage field.
+
+=cut
+
+sub stages {
+ return qw(incomplete unprocessed backorder picked shipped cancelled stalled returned);
+}
+
+=item settable_stages()
+
+Return the stages that the admin can set the order stage to.
+
+=cut
+
+sub settable_stages {
+ my ($class) = @_;
+
+ return grep $_ ne "incomplete", $class->stages;
+}
+
+=item stage_labels($lang)
+
+Return descriptive labels for the stage values.
+
+=cut
+
+sub stage_labels {
+ my ($self, $lang) = @_;
+
+ return map { $_ => $self->stage_label($_, $lang) } $self->stages;
+}
+
+=item stage_label($stage, $lang)
+
+Given a stage value, return the label.
+
+=cut
+
+sub stage_label {
+ my ($self, $stage, $lang) = @_;
+
+ require BSE::Message;
+ my $msgs = BSE::Message->new;
+
+ return $msgs->text($lang, "msg:bse/shop/orderstages/$stage", [], $stage);
+}
+
1;
use BSE::TB::Images;
use Articles;
use BSE::Sort;
-use BSE::Util::Tags qw(tag_hash tag_error_img);
+use BSE::Util::Tags qw(tag_hash tag_error_img tag_object_plain tag_object);
use BSE::Util::Iterate;
use BSE::WebUtil 'refresh_to_admin';
use BSE::Util::HTML qw(:default popup_menu);
use BSE::Arrows;
use BSE::Shop::Util qw(:payment order_item_opts nice_options);
-our $VERSION = "1.004";
+our $VERSION = "1.005";
my %actions =
(
);
}
+sub tag_stage_select {
+ my ($self, $req, $order) = @_;
+
+ my @stages = BSE::TB::Orders->settable_stages;
+
+ my %stage_labels = BSE::TB::Orders->stage_labels;
+ return popup_menu
+ (
+ -name => "stage",
+ -values => \@stages,
+ -default => $order->stage,
+ -labels => \%stage_labels,
+ );
+}
+
sub req_order_detail {
my ($class, $req, $errors) = @_;
}
return 0;
},
- order => [ \&tag_hash, $order ],
+ order => [ \&tag_object, $order ],
extension =>
sub {
sprintf("%.2f", $lines[$line_index]{units} * $lines[$line_index]{$_[0]}/100.0)
),
shipping_method_select =>
[ tag_shipping_method_select => $class, $order ],
+ stage_select =>
+ [ tag_stage_select => $class, $req, $order ],
+ stage_description => escape_html($order->stage_description($req->language)),
);
return $req->dyn_response('admin/order_detail', \%acts);
Make changes to an order, only a limited set of fields can be changed.
-Parameters:
+Parameters, all optional:
=over
freight_tracking - the freight tracking code for the shipment.
+=item *
+
+stage - order stage, one of unprocessed, backorder, picked, shipped,
+cancelled.
+
=back
Requires csrfp token C<shop_order_save>.
}
}
+ my $new_stage = 0;
+ my ($stage) = $cgi->param("stage");
+ my ($stage_note) = $cgi->param("stage_note");
+ if (defined $stage && $stage ne $order->stage
+ || defined $stage_note && $stage_note =~ /\S/) {
+ my @stages = BSE::TB::Orders->settable_stages;
+ if (grep $_ eq $stage, @stages) {
+ ++$new_stage;
+ ++$save;
+ }
+ else {
+ $errors{stage} = "msg:bse/admin/shop/saveorder/badstage:$stage";
+ }
+ }
+
keys %errors
and return $self->req_order_detail($req, \%errors);
if ($save) {
- $order->save;
- $req->flash("msg:bse/admin/shop/saveorder/saved");
-
if ($new_freight_tracking) {
$req->audit
(
level => "info",
);
}
+ if ($new_stage) {
+ $order->new_stage(scalar $req->user, $stage, $stage_note);
+ }
+
+ $order->save;
+ $req->flash("msg:bse/admin/shop/saveorder/saved");
}
else {
$req->flash("msg:bse/admin/shop/saveorder/nochanges");
use BSE::Countries qw(bse_country_code);
use BSE::Util::Secure qw(make_secret);
-our $VERSION = "1.010";
+our $VERSION = "1.012";
use constant MSG_SHOP_CART_FULL => 'Your shopping cart is full, please remove an item and try adding an item again';
my %errors;
my %values;
for my $name (keys %fields) {
- ($values{$name}) = $cgi->param($name);
+ my ($value) = $cgi->param($name);
+ defined $value or $value = "";
+ $values{$name} = $value;
}
my @required =
cardNumber => 1,
cardExpiry => 1,
delivery_in => 1,
+ cardVerify => 1,
);
sub req_payment {
my @items = $class->_build_items($req, \@products);
- my @columns = BSE::TB::Order->columns;
- my %columns;
- @columns{@columns} = @columns;
-
- for my $col (@columns) {
- defined $order_values->{$col} or $order_values->{$col} = '';
- }
-
- my @data = @{$order_values}{@columns};
- shift @data;
-
if ($session->{order_work}) {
$order = BSE::TB::Orders->getByPkey($session->{order_work});
}
if ($order && !$order->{complete}) {
+ my @columns = BSE::TB::Order->columns;
+ my %columns;
+ @columns{@columns} = @columns;
+
+ for my $col (@columns) {
+ defined $order_values->{$col} or $order_values->{$col} = '';
+ }
+
+ my @data = @{$order_values}{@columns};
+ shift @data;
+
print STDERR "Recycling order $order->{id}\n";
my @allbutid = @columns;
};
}
else {
- $order = BSE::TB::Orders->add(@data)
+ $order = BSE::TB::Orders->make(%$order_values)
or die "Cannot add order";
}
}
# order complete
- $order->{complete} = 1;
+ $order->set_complete(1);
+ $order->set_stage("unprocessed");
$order->save;
$class->_finish_order($req, $order);
}
$values->{total} = $total;
$values->{gst} = $total_gst;
- $values->{wholesale} = $total_wholesale;
+ #$values->{wholesale} = $total_wholesale;
my $prompt_ship = $cfg->entry("shop", "shipping", 0);
if ($prompt_ship) {
$values->{shipping_name} = $courier->name;
$values->{shipping_cost} = $cost;
$values->{shipping_trace} = $courier->trace;
- $values->{delivery_in} = $courier->delivery_in();
+ #$values->{delivery_in} = $courier->delivery_in();
$values->{total} += $values->{shipping_cost};
}
else {
description: Displayed on a save that makes no changes
id: bse/admin/shop/saveorder/badmethod
-description: Invalid shipping method
+description: Invalid shipping method, $1 is the supplied method id
+
+id: bse/admin/shop/saveorder/badstage
+description: Invalid order stage, $1 is the supplied stage id
id: test/
description: <<TEXT
id: bse/shop/paypal/badtoken
description: The transaction token supplied by PayPal to paypalret doesn't match the token value stored in the order
+id: bse/shop/orderstages/
+description: Descriptions of the order stages
+
+id: bse/shop/orderstages/incomplete
+description: The incomplete order stage
+
+id: bse/shop/orderstages/unprocessed
+description: The unprocessed order stage
+
+id: bse/shop/orderstages/backorder
+description: The backorderorder stage
+
+id: bse/shop/orderstages/picked
+description: The picked order stage
+
+id: bse/shop/orderstages/shipped
+description: The shipped order stage
+
+id: bse/shop/orderstages/cancelled
+description: The cancelled order stage
+
+id: bse/shop/orderstages/stalled
+description: The stalled order stage
+
+id: bse/shop/orderstages/returned
+description: The stalled returned stage
+
id: bse/files/
description: File validation messages
---
-# VERSION=1.001
+# VERSION=1.002
# defaults for the following
language_code: en
priority: 0
id: bse/admin/shop/saveorder/badmethod
message: Unknown shipping method '$1:s'
+id: bse/admin/shop/saveorder/badstage
+message: Unknown order stage '$1:s'
+
id: bse/shop/cart/empty
message: Cart emptied
id: bse/shop/paypal/badtoken
message: The token returned by PayPal doesn't match the token generated for order.
+id: bse/shop/orderstages/incomplete
+message: Not completed by customer
+
+id: bse/shop/orderstages/unprocessed
+message: Awaiting processing
+
+id: bse/shop/orderstages/backorder
+message: One or more items on backorder
+
+id: bse/shop/orderstages/picked
+message: Items picked and packed
+
+id: bse/shop/orderstages/shipped
+message: Shipped
+
+id: bse/shop/orderstages/cancelled
+message: Cancelled
+
+id: bse/shop/orderstages/stalled
+message: Stalled on customer response
+
+id: bse/shop/orderstages/returned
+message: Returned
+
id: bse/files/image_file_required
message: File must be an image file
</tr>
<:eif Cfg:>
</table>
+<p>Order progress: <:stage_description:> <a href="#" onclick="document.getElementById('update_stage').style.display='block'; this.style.display='none'; return false;">update</a></p>
+
+<form id="update_stage" action="<:adminurl shopadmin:>" style="display: none" method="post">
+ <input type="hidden" name="id" value="<:order id:>" />
+ <:csrfp shop_order_save hidden:>
+ <:stage_select:>
+ <label>Log note: <input type="text" name="stage_note" size="40" /> (optional)</label>
+ <input type="submit" name="a_order_save" value="Save" />
+</form>
+
<:switch:>
<:case Eq [order paidFor] "0":>
<p>This order hasn't been paid</p>
--- /dev/null
+Your order <:order id |%05d:> has been shipped.
+<:ifOrder freight_tracking
+:>It has tracking code <:order freight_tracking:>
+
+<:or:><:eif
+:>
+Item Units Price
+<:iterator begin orderitems:>
+<:orderitem title |%-60s:>
+ <:orderitem units |%6d:> <:money orderitem price |%8s:>
+<:iterator end orderitems:>
+<:ifOrder shipping_cost
+:>Shipping: <:money order shipping_cost |%8s:><:or:><:eif:>
+Total: <:money order total |%8s:>
Column paypal_token;varchar(255);YES;NULL;
Column paypal_tran_id;varchar(255);YES;NULL;
Column freight_tracking;varchar(255);NO;;
+Column stage;varchar(20);NO;;
Index PRIMARY;1;[id]
Index order_cchash;0;[ccNumberHash]
Index order_userId;0;[userId;orderDate]