FINAL suepr merge step : added all modules to this super repos
311
sites/all/modules/contrib/ecommerce/ubercart/CHANGELOG.txt
Normal file
@@ -0,0 +1,311 @@
|
||||
Ubercart 3.0-rc4, 2012-1-23
|
||||
---------------------------
|
||||
- API:
|
||||
* Added hook_uc_payment_method_alter().
|
||||
* Added hook_uc_product_alter().
|
||||
* Added theme_uc_attribute_option() for the add to cart forms.
|
||||
* Added theme_uc_qty().
|
||||
* Node shipping addresses are always arrays.
|
||||
* uc_line_item_tax() removed.
|
||||
* uc_cybersource_uc_calculate_tax() returns an associative array.
|
||||
* Removed theme_uc_payment_method_select().
|
||||
* Removed 'load' op from hook_uc_cart_item().
|
||||
* Removed $op from uc_cart_remove_item().
|
||||
* Removed uc_country_select().
|
||||
* Remvoed uc_country_ajax_zone().
|
||||
* Replaced 'can_ship' op of hook_uc_cart_item() with
|
||||
hook_uc_order_product_can_ship().
|
||||
* Replaced uc_cart_item_is_shippable() with uc_order_product_is_shippable().
|
||||
* Order pane op 'show-title' removed. Use the 'edit title' property in
|
||||
hook_uc_order_pane().
|
||||
* Order pane op 'edit-ops' removed. Use #ajax for the same functionality.
|
||||
- Database:
|
||||
* {uc_product_adjustments}.combination has 'serialize' => TRUE.
|
||||
- Entity:
|
||||
* Order products and cart items are now handled by the Entity API.
|
||||
- Add 'cart' view mode for order products.
|
||||
- Orders:
|
||||
* Added 'Abandoned' status. 'In Checkout' orders are moved to 'Abandoned'
|
||||
after a configurable period of time.
|
||||
- Rules:
|
||||
* Added event 'uc_order_delete'.
|
||||
* Added event 'uc_product_load'. Occurs before hook_uc_product_alter().
|
||||
* Allow payment methods to have configurable conditions, like shipping
|
||||
quotes.
|
||||
- UI:
|
||||
* Converted admin/store/customers page to a View.
|
||||
- Misc:
|
||||
* Added tests.
|
||||
* Removed inline CSS and deprecated HTML attributes.
|
||||
* Removed 'uc_catalog_name' variable.
|
||||
* Removed 'uc_minimum_subtotal_text' variable.
|
||||
|
||||
Ubercart 3.0-rc3, 2011-12-5
|
||||
---------------------------
|
||||
- API:
|
||||
* Added 'prepare' op to checkout pane callbacks. This step runs before
|
||||
'view'.
|
||||
* Added uc_store_email().
|
||||
* Added 'operations' key to hook_uc_shipping_method() to list configuration
|
||||
pages.
|
||||
* Improved UcAddress. Made comparison of addresses more robust.
|
||||
* Moved admin/store/customers code from uc_store to uc_order.
|
||||
* Removed deprecated code that supported uc_recurring.
|
||||
* Removed deprecated uc_add_js().
|
||||
* Removed uc_cart_exit() and cache safe cart block.
|
||||
- Database:
|
||||
* Added {uc_shipments}.changed.
|
||||
* Removed {uc_order_products}.manufacturer, finally.
|
||||
- Forms:
|
||||
* Added form element types:
|
||||
- 'uc_price'
|
||||
- 'uc_quantity'
|
||||
- Theme:
|
||||
* Hide weight and dimensions fields if their values are zero.
|
||||
* Indicate that Google Checkout and Paypal Express Checkout are different
|
||||
from Ubercart Checkout.
|
||||
* Use invisible form labels in tables and abbreviation tags for
|
||||
accessibility.
|
||||
- UI:
|
||||
* Added 'Create an order for this customer' link to
|
||||
admin/store/customers/orders/%uid.
|
||||
* Converted admin/store/orders/search page to a View.
|
||||
* Moved form for administering a user's file downloads from user/%/edit to
|
||||
user/%/purchased-files.
|
||||
* Reconfigured shipping quotes settings.
|
||||
- Main configuration page is at admin/store/settings/quotes.
|
||||
- Settings for individual quote methods (implemented by uc_flatrate and
|
||||
uc_weightquote) go under admin/store/settings/quotes/methods (the
|
||||
default local task).
|
||||
- Settings for modules (implemented by uc_ups and uc_usps) go under
|
||||
admin/store/settings/quotes/settings.
|
||||
* Replaced fieldsets with vertical tabs for USPS and UPS admin settings.
|
||||
- Views:
|
||||
* Added Views integration for uc_product_kit.
|
||||
* Added alternative grid view to catalog.
|
||||
* Made {uc_order_products} a possible base table.
|
||||
* Renamed uc_stock_views_handler_filter_below_threshold to
|
||||
uc_stock_handler_filter_below_threshold.
|
||||
* Removed sort and filter from 'full_name' on uc_orders.
|
||||
- Misc:
|
||||
* Add option to include authorization only transactions in order balance
|
||||
condition.
|
||||
* Removed credit card debug mode.
|
||||
|
||||
Ubercart 3.0-rc2, 2011-10-7
|
||||
---------------------------
|
||||
- Views:
|
||||
* Orders must use a relationship to get product data.
|
||||
|
||||
Ubercart 3.0-rc1, 2011-10-4
|
||||
---------------------------
|
||||
- API:
|
||||
* uc_order_load_line_items() returns stored and calculated line items together.
|
||||
* Product nodes have display_price and display_price_suffix, which show
|
||||
price changes that are ultimately calculated during checkout (e.g., VAT).
|
||||
* Added currency field to orders.
|
||||
* Added default values to uc_payment_enter().
|
||||
* Removed uc_strip_form().
|
||||
-Database:
|
||||
* Increased size of {uc_payment_receipts}.comment.
|
||||
- Theme:
|
||||
* Invoice templates can use $shippable variable.
|
||||
* Added theme_uc_checkout_pane_cart_review().
|
||||
* Removed theme_uc_cart_view_price().
|
||||
* Removed theme_uc_address_pane().
|
||||
- UI:
|
||||
* Converted "View orders" page to a View.
|
||||
* Address field settings moved from checkout settings to country settings.
|
||||
- Misc:
|
||||
* Added dependence on Views to Orders.
|
||||
* Restored dependence on Rules.
|
||||
* Product tokens are [node:product:...] instead of [product:...].
|
||||
* Removed [store:url] token. Duplicate of [site:url].
|
||||
* Use the permission 'view reports' instead of 'view store reports'.
|
||||
|
||||
Ubercart 3.0-beta4, 2011-7-25
|
||||
-----------------------------
|
||||
- API:
|
||||
* Removed ability to disable order logging.
|
||||
* Removed the ability to disable payment logging or tracking.
|
||||
* Off-site payment methods can specify simpler redirect forms.
|
||||
* Payment gateways are now only used and supported by uc_credit.
|
||||
* Ordered product titles increased from 128 to 255 characters.
|
||||
* The following hooks are now keyed by ID, though backwards compatibility is kept:
|
||||
* hook_uc_cart_pane()
|
||||
* hook_uc_checkout_pane()
|
||||
* hook_uc_line_item()
|
||||
* hook_uc_payment_gateway()
|
||||
* hook_uc_payment_method()
|
||||
* hook_uc_order_pane()
|
||||
* hook_uc_order_state()
|
||||
* Added hook_uc_order_pane_alter().
|
||||
* Removed settings form summaries.
|
||||
- Theme:
|
||||
* Improved theming of printable invoices.
|
||||
* Shipping options theme functions for UPS and USPS include number of packages being sent.
|
||||
* Added CSS classes for payment methods.
|
||||
- UI:
|
||||
* Converted 'create order' page to Ajax.
|
||||
* Many settings pages overhauled and simplified.
|
||||
* Converted cart, checkout and order pane admin to use drag and drop ordering.
|
||||
* Converted payment method and shipping quotes to use drag and drop ordering.
|
||||
* Country names are fully translatable.
|
||||
- Misc:
|
||||
* Product field ordering is handled by standard field display management.
|
||||
* Added option to allow anonymous checkouts to use an existing email address.
|
||||
* Automatic deletion of generated shipping labels.
|
||||
* Product tokens are [product: ... ] instead of [node: ... ].
|
||||
* Added additional "value" tokens for numeric prices.
|
||||
* Changed [uc_order:number] token to [uc_order:order-number].
|
||||
* Added some SimpleTests.
|
||||
* Numerous documentation fixes and improvements.
|
||||
* A multitude of bug fixes.
|
||||
|
||||
Ubercart 3.0-beta3, 2011-5-26
|
||||
-----------------------------
|
||||
- API:
|
||||
* Add Ajax-enabled "uc_address" form element for full or partial address entry.
|
||||
* Handle temporary cart items with quantity of zero.
|
||||
* $item->data is consistently unserialized in hook_uc_cart_item().
|
||||
* 'Default quantity to add to cart' is respected when quantity field is hidden.
|
||||
* Add hook_uc_payment_gateway_alter().
|
||||
* Add hook_uc_checkout_pane_alter().
|
||||
- Theme:
|
||||
* Pass order object to theme_uc_cart_complete_sale().
|
||||
* Default product image size increased from 100x100 to 250x250.
|
||||
- UI:
|
||||
* Converted all address forms to Ajax.
|
||||
* Date formatting is now handled by Drupal core settings.
|
||||
- Misc:
|
||||
* Purchased files with unlimited downloads cannot be purchased twice.
|
||||
* Added some SimpleTests.
|
||||
* Removed hard dependency on Rules.
|
||||
* Numerous documentation fixes and improvements.
|
||||
* A multitude of bug fixes.
|
||||
|
||||
Ubercart 3.0-beta2, 2011-3-11
|
||||
-----------------------------
|
||||
- API:
|
||||
* Add support for HTML emails.
|
||||
* Google Checkout integration:
|
||||
- Updated to API v2.5.
|
||||
- Notifications require "Serial Number Content" setting in the Merchant
|
||||
Center
|
||||
- Merchant-calculated shipping quotes use Ubercart shipping quotes. Set
|
||||
a default shipping address to get a fallback rate before actual rates
|
||||
are calculated.
|
||||
- With google_analytics.module enabled, Google Analytics data is
|
||||
recorded for Google Checkout orders.
|
||||
* Removed deprecated uc_stock_decrement_product_stock().
|
||||
* Removed unused uc_store_tables().
|
||||
* USPS integration updated to RateV4 and IntlRateV2 APIs.
|
||||
- Fields:
|
||||
* Product, product classes, and product kits have body fields after D7
|
||||
upgrade like they should.
|
||||
- Theme:
|
||||
* Credit card icons have specific CSS classes.
|
||||
- UI:
|
||||
* Added setting to show or hide credit card form on PayPal Express Checkout
|
||||
landing page.
|
||||
* Changed "Remove" checkbox on cart page to a button.
|
||||
* Hide options and adjustments tab on product edit page if the product does
|
||||
not have attributes.
|
||||
* Improved country settings pages.
|
||||
* Removed cart block title setting from cart settings page. Set it through
|
||||
the block administration page.
|
||||
- Misc:
|
||||
* All 248 countries in ISO 3166 have .cif files.
|
||||
|
||||
Ubercart 3.0-beta1, 2010-12-09
|
||||
------------------------------
|
||||
- API:
|
||||
* Implemented entity features for orders:
|
||||
- Added $reset parameter to uc_order_load().
|
||||
- Added uc_order_load_multiple().
|
||||
- Fields saved and loaded.
|
||||
* Order pane callbacks also changed signatures:
|
||||
uc_order_pane_PANE_ID($op, $order, &$form = NULL, &$form_state = NULL)
|
||||
- This signature applies to all ops, including those defined in
|
||||
'edit-ops'.
|
||||
* Quote method callbacks no longer need to return formatted rates.
|
||||
* uc_currency_format() changed signature:
|
||||
uc_currency_format($value, $sign = NULL, $thou = NULL, $dec = NULL)
|
||||
* Removed uc_price().
|
||||
* Removed hook_uc_price_handler().
|
||||
* theme_uc_price() takes one parameter: 'price', a float.
|
||||
* theme_uc_product_price() expects a render element with a #value and
|
||||
optional #title and #attributes. Element children may be used to display
|
||||
text after the price.
|
||||
* 'view' $op added to hook_uc_cart_item()
|
||||
- Allows modules to modify items before they are shown on the cart page.
|
||||
- Database:
|
||||
* Dropped {cache_uc_price}.
|
||||
* Added {uc_taxed_product_types}.
|
||||
* Added {uc_taxed_line_items}.
|
||||
- Forms:
|
||||
* Submit buttons wrapped in a #type = 'actions' container.
|
||||
* Edit order form now contains field widgets.
|
||||
- Menu:
|
||||
* Customers may view their orders at user/%user/orders/%uc_order to match
|
||||
the parent item user/%user/orders.
|
||||
- Taxes:
|
||||
* Tax rates may be set to be included in product prices. Their associated
|
||||
conditions are not checked until the checkout page, where they are shown
|
||||
as separate line items and not part of product prices.
|
||||
|
||||
Ubercart 3.0-alpha3, 2010-07-16
|
||||
-------------------------------
|
||||
- API:
|
||||
* Dropped Conditional Actions.
|
||||
* Integrated with Rules.
|
||||
* Product features:
|
||||
- Added uc_product_feature_load_multiple().
|
||||
- Added uc_product_feature_load().
|
||||
- Added uc_product_feature_delete().
|
||||
- Theme:
|
||||
* Ported order invoice templates to new token names.
|
||||
|
||||
Ubercart 3.0-alpha2, 2010-06-11
|
||||
-------------------------------
|
||||
- API:
|
||||
* Changed payment method callback signature:
|
||||
uc_payment_method_METHOD($op, &$order, $form = NULL, &$form_state = NULL)
|
||||
* Changed checkout pane callback signature:
|
||||
uc_checkout_pane_PANE_ID($op, &$order, $form = NULL, &$form_state = NULL)
|
||||
* Use $form_state['values']['panes'][PANE_ID] instead of $arg2 during the
|
||||
'process' $op.
|
||||
* New UcAddress class to initialize address data.
|
||||
* New UcOrder class to initialize order data.
|
||||
* Most of uc_order.js, uc_payment.js, uc_quote.js, and uc_taxes.js removed
|
||||
in favor of Form API AJAX integration.
|
||||
* Removed uc_order_edit_products_form() in favor of AJAX processing.
|
||||
* uc_payment_get_totals() is now an AJAX callback.
|
||||
* _uc_quote_assemble_quotes() renamed to uc_quote_assemble_quotes()
|
||||
* Removed uc_quote_request_quotes().
|
||||
* uc_product_feature_save() now takes $data as a reference.
|
||||
* Editing and adding products to orders now use AJAX, so nearly all
|
||||
functions in that process have been replaced, removed, or reused.
|
||||
- hook_uc_order_product_alter() has not changed.
|
||||
- Database:
|
||||
* Dropped fields:
|
||||
- {uc_order_quotes}.quote_form
|
||||
|
||||
Ubercart 3.0-alpha1, 2010-04-14
|
||||
-------------------------------
|
||||
- API:
|
||||
* Changed Ubercart hooks use the form hook_uc_hookname().
|
||||
* uc_payment_process() changed to uc_payment_process_payment().
|
||||
- Database:
|
||||
* Dropped the following tables:
|
||||
- {uc_catalog_images}
|
||||
* Added fields:
|
||||
- {uc_attributes}.format
|
||||
* Changes were made to the following fields:
|
||||
- {uc_packages}.label_image is an int referring to {file}.file_id.
|
||||
- Fields:
|
||||
* The default product image moved from field_image_cache to uc_product_image.
|
||||
* The catalog term image is an actual field, located at uc_catalog_image.
|
||||
- Files:
|
||||
* Shipment labels are now managed files.
|
||||
* Catalog term images are now managed files in the {file} table.
|
34
sites/all/modules/contrib/ecommerce/ubercart/CREDITS.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
While we'd like to maintain that Ubercart was coded by wombats, there are actual
|
||||
people behind the words getting typed out. You can find these people lurking on
|
||||
Ubercart.org to give them a pat on the back:
|
||||
|
||||
Original development team:
|
||||
Ryan, Lyle, Shawn Conn
|
||||
|
||||
7.x-3.x development team:
|
||||
Lyle, Tim Rohaly, Dave Long
|
||||
|
||||
Bug fixing and contributions by:
|
||||
arbel, balcorn, blucchesi@drupal.org, BullCreek, bwv, cosmo83@drupal.org, CpILL,
|
||||
daniel, darxtar, david, David Strauss, druru, gregmac, izi, japerry@drupal.org
|
||||
keesje76, K.P., Kulvik,lafflam@drupal.org, megg, mikejoconnor, psi-borg, Quaoar,
|
||||
Tanjerine, Theoverclocked, torgosPizza, tormi, _Troy_@drupal.org, zmove
|
||||
|
||||
Many thanks to the people who have contributed country import files to help
|
||||
spread Ubercart around the world!
|
||||
|
||||
Thanks to other development teams working Ubercart into their client sites and
|
||||
contributing fixes and features back to Ubercart:
|
||||
|
||||
- Ny Media AS (Norway) - http://www.nymedia.no
|
||||
- Sundays Energy - http://www.sundaysenergy.com
|
||||
- Four Kitchens Studios - http://fourkitchens.com
|
||||
- Taylor Square Designs - http://www.tsd.net.au
|
||||
- Bold Source - http://www.boldsource.com
|
||||
- Vingo Wine & Spirits - http://www.vingowine.com
|
||||
- Qrios Webdevelopment - http://www.qrios.nl
|
||||
- Warner Brothers Records and Work Habit
|
||||
|
||||
Also, a special thanks to all you Ubercart users out there! Take some time to
|
||||
see what folks are doing with this module package by browsing our live sites
|
||||
directory: http://www.ubercart.org/site
|
339
sites/all/modules/contrib/ecommerce/ubercart/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
47
sites/all/modules/contrib/ecommerce/ubercart/README.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
UBERCART README
|
||||
|
||||
Ubercart is a set of modules designed to allow you to run a store on your Drupal
|
||||
site. Development has been driven by two major emphases: flexibility and
|
||||
usability. The various systems driving Ubercart, including the product catalog,
|
||||
shopping cart and checkout, order, shipping, and payment systems have all been
|
||||
designed to be extended to meet any need. Furthermore, the administrative
|
||||
interface, particularly relating to processing and fulfilling orders, has been
|
||||
designed for ease of use and efficient display of information.
|
||||
|
||||
Ubercart is being sponsored and developed by the team at
|
||||
http://www.ubercart.org. Sign up for site access to join the Ubercart community
|
||||
and take advantage of our forums and issue tracking.
|
||||
|
||||
Features overview:
|
||||
http://www.ubercart.org/what_is_ubercart
|
||||
|
||||
Screenshots:
|
||||
http://www.ubercart.org/screenshots
|
||||
|
||||
User's and Developer's Guides:
|
||||
http://www.ubercart.org/docs
|
||||
|
||||
Installation Manual (with module descriptions):
|
||||
http://www.ubercart.org/docs/user/8075/installing_ubercart
|
||||
|
||||
Support forums:
|
||||
http://www.ubercart.org/forum
|
||||
|
||||
Contributions directory:
|
||||
http://www.ubercart.org/contrib
|
||||
|
||||
Live sites directory:
|
||||
http://www.ubercart.org/site
|
||||
|
||||
Report a bug or feature request:
|
||||
http://drupal.org/project/issues/ubercart
|
||||
|
||||
Thanks for checking out Ubercart! We would love to hear your feedback.
|
||||
We particularly want to know how we can make Ubercart better and easier to use.
|
||||
Feel free to post your thoughts in the forums at Ubercart.org! We will make a
|
||||
survey available for more targeted feedback in the near future.
|
||||
|
||||
Releases may be downloaded at http://drupal.org/project/ubercart.
|
||||
|
||||
Kind regards,
|
||||
The UberDudes
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,12 @@
|
||||
name = 2Checkout
|
||||
description = Processes payments using 2Checkout.com.
|
||||
dependencies[] = uc_payment
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_2checkout module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_2checkout_uninstall() {
|
||||
db_delete('variable')
|
||||
->condition('name', 'uc_2checkout_%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unused variable.
|
||||
*/
|
||||
function uc_2checkout_update_7300() {
|
||||
variable_del('uc_2checkout_checkout_button');
|
||||
}
|
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Integrates 2Checkout.com's redirected payment service.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function uc_2checkout_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/store/settings/payment/method/%':
|
||||
if ($arg[5] == '2checkout') {
|
||||
return '<p>' . t('To accept PayPal payments in 2Checkout, please ensure that demo mode is disabled and your store currency is one of USD, AUD, CAD, EUR or GBP.') . '</p>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function uc_2checkout_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['cart/2checkout/complete'] = array(
|
||||
'title' => 'Order complete',
|
||||
'page callback' => 'uc_2checkout_complete',
|
||||
'access callback' => TRUE,
|
||||
'type' => MENU_CALLBACK,
|
||||
'file' => 'uc_2checkout.pages.inc',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_init().
|
||||
*/
|
||||
function uc_2checkout_init() {
|
||||
global $conf;
|
||||
$conf['i18n_variables'][] = 'uc_2checkout_method_title';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ucga_display().
|
||||
*/
|
||||
function uc_2checkout_ucga_display() {
|
||||
// Tell UC Google Analytics to display the e-commerce JS on the custom
|
||||
// order completion page for this module.
|
||||
if (arg(0) == 'cart' && arg(1) == '2checkout' && arg(2) == 'complete') {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_payment_method().
|
||||
*
|
||||
* @see uc_payment_method_2checkout()
|
||||
*/
|
||||
function uc_2checkout_uc_payment_method() {
|
||||
$path = base_path() . drupal_get_path('module', 'uc_2checkout');
|
||||
$title = variable_get('uc_2checkout_method_title', t('Credit card on a secure server:'));
|
||||
$title .= '<br />' . theme('image', array(
|
||||
'path' => drupal_get_path('module', 'uc_2checkout') . '/2co_logo.jpg',
|
||||
'attributes' => array('class' => array('uc-2checkout-logo')),
|
||||
));
|
||||
|
||||
$methods['2checkout'] = array(
|
||||
'name' => t('2Checkout'),
|
||||
'title' => $title,
|
||||
'review' => variable_get('uc_2checkout_check', FALSE) ? t('Credit card/eCheck') : t('Credit card'),
|
||||
'desc' => t('Redirect to 2Checkout to pay by credit card or eCheck.'),
|
||||
'callback' => 'uc_payment_method_2checkout',
|
||||
'redirect' => 'uc_2checkout_form',
|
||||
'weight' => 3,
|
||||
'checkout' => TRUE,
|
||||
'no_gateway' => TRUE,
|
||||
);
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds 2Checkout settings to the payment method settings form.
|
||||
*
|
||||
* @see uc_2checkout_uc_payment_method()
|
||||
*/
|
||||
function uc_payment_method_2checkout($op, &$order, $form = NULL, &$form_state = NULL) {
|
||||
switch ($op) {
|
||||
case 'cart-details':
|
||||
$build = array();
|
||||
|
||||
if (variable_get('uc_2checkout_check', FALSE)) {
|
||||
$build['pay_method'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Select your payment type:'),
|
||||
'#default_value' => $_SESSION['pay_method'] == 'CK' ? 'CK' : 'CC',
|
||||
'#options' => array(
|
||||
'CC' => t('Credit card'),
|
||||
'CK' => t('Online check'),
|
||||
),
|
||||
);
|
||||
unset($_SESSION['pay_method']);
|
||||
}
|
||||
|
||||
return $build;
|
||||
|
||||
case 'cart-process':
|
||||
if (isset($form_state['values']['panes']['payment']['details']['pay_method'])) {
|
||||
$_SESSION['pay_method'] = $form_state['values']['panes']['payment']['details']['pay_method'];
|
||||
}
|
||||
return;
|
||||
|
||||
case 'settings':
|
||||
$form['uc_2checkout_sid'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Vendor account number'),
|
||||
'#description' => t('Your 2Checkout vendor account number.'),
|
||||
'#default_value' => variable_get('uc_2checkout_sid', ''),
|
||||
'#size' => 16,
|
||||
);
|
||||
$form['uc_2checkout_secret_word'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Secret word for order verification'),
|
||||
'#description' => t('The secret word entered in your 2Checkout account Look and Feel settings.'),
|
||||
'#default_value' => variable_get('uc_2checkout_secret_word', 'tango'),
|
||||
'#size' => 16,
|
||||
);
|
||||
$form['uc_2checkout_demo'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable demo mode, allowing you to process fake orders for testing purposes.'),
|
||||
'#default_value' => variable_get('uc_2checkout_demo', TRUE),
|
||||
);
|
||||
$form['uc_2checkout_language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Language preference'),
|
||||
'#description' => t('Adjust language on 2Checkout pages.'),
|
||||
'#options' => array(
|
||||
'en' => t('English'),
|
||||
'sp' => t('Spanish'),
|
||||
),
|
||||
'#default_value' => variable_get('uc_2checkout_language', 'en'),
|
||||
);
|
||||
$form['uc_2checkout_check'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Allow customers to choose to pay by credit card or online check.'),
|
||||
'#default_value' => variable_get('uc_2checkout_check', FALSE),
|
||||
);
|
||||
$form['uc_2checkout_method_title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Payment method title'),
|
||||
'#default_value' => variable_get('uc_2checkout_method_title', t('Credit card on a secure server:')),
|
||||
);
|
||||
$form['uc_2checkout_checkout_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('2Checkout.com checkout type'),
|
||||
'#description' => t('Single page checkout only works for stores selling intangible products using credit card payments.'),
|
||||
'#options' => array(
|
||||
'multi' => t('Multi-page checkout'),
|
||||
'single' => t('Single page checkout'),
|
||||
),
|
||||
'#default_value' => variable_get('uc_2checkout_checkout_type', 'multi'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to build the submission to 2Checkout.com.
|
||||
*/
|
||||
function uc_2checkout_form($form, &$form_state, $order) {
|
||||
$country = uc_get_country_data(array('country_id' => $order->billing_country));
|
||||
if ($country === FALSE) {
|
||||
$country = array(0 => array('country_iso_code_3' => 'USA'));
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'sid' => variable_get('uc_2checkout_sid', ''),
|
||||
'total' => uc_currency_format($order->order_total, FALSE, FALSE, '.'),
|
||||
'cart_order_id' => $order->order_id,
|
||||
'demo' => variable_get('uc_2checkout_demo', TRUE) ? 'Y' : 'N',
|
||||
'fixed' => 'Y',
|
||||
'lang' => variable_get('uc_2checkout_language', 'en'),
|
||||
'x_receipt_link_url' => url('cart/2checkout/complete/' . uc_cart_get_id(), array('absolute' => TRUE)),
|
||||
'merchant_order_id' => $order->order_id,
|
||||
'pay_method' => isset($_SESSION['pay_method']) ? $_SESSION['pay_method'] : 'CC',
|
||||
'card_holder_name' => drupal_substr($order->billing_first_name . ' ' . $order->billing_last_name, 0, 128),
|
||||
'street_address' => drupal_substr($order->billing_street1, 0, 64),
|
||||
'street_address2' => drupal_substr($order->billing_street2, 0, 64),
|
||||
'city' => drupal_substr($order->billing_city, 0, 64),
|
||||
'state' => uc_get_zone_code($order->billing_zone),
|
||||
'zip' => drupal_substr($order->billing_postal_code, 0, 16),
|
||||
'country' => $country[0]['country_iso_code_3'],
|
||||
'email' => drupal_substr($order->primary_email, 0, 64),
|
||||
'phone' => drupal_substr($order->billing_phone, 0, 16),
|
||||
'id_type' => 1,
|
||||
);
|
||||
|
||||
$i = 0;
|
||||
foreach ($order->products as $product) {
|
||||
$i++;
|
||||
$data['c_prod_' . $i] = $product->model . ',' . $product->qty;
|
||||
$data['c_name_' . $i] = $product->title;
|
||||
$data['c_description_' . $i] = '';
|
||||
$data['c_price_' . $i] = uc_currency_format($product->price, FALSE, FALSE, '.');
|
||||
}
|
||||
|
||||
$form['#action'] = _uc_2checkout_post_url(variable_get('uc_2checkout_checkout_type', 'multi'));
|
||||
|
||||
foreach ($data as $name => $value) {
|
||||
$form[$name] = array('#type' => 'hidden', '#value' => $value);
|
||||
}
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Submit order'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to obtain 2Checkout URL for a transaction.
|
||||
*/
|
||||
function _uc_2checkout_post_url($type) {
|
||||
switch ($type) {
|
||||
case 'single':
|
||||
return 'https://www.2checkout.com/checkout/spurchase';
|
||||
case 'multi':
|
||||
default:
|
||||
return 'https://www.2checkout.com/2co/buyer/purchase';
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* 2checkout menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Finalizes 2checkout transaction.
|
||||
*/
|
||||
function uc_2checkout_complete($cart_id = 0) {
|
||||
watchdog('2Checkout', 'Receiving new order notification for order !order_id.', array('!order_id' => check_plain($_REQUEST['merchant_order_id'])));
|
||||
|
||||
$order = uc_order_load($_REQUEST['merchant_order_id']);
|
||||
|
||||
if ($order === FALSE || uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
|
||||
return t('An error has occurred during payment. Please contact us to ensure your order has submitted.');
|
||||
}
|
||||
|
||||
$key = $_REQUEST['key'];
|
||||
$order_number = variable_get('uc_2checkout_demo', TRUE) ? 1 : $_REQUEST['order_number'];
|
||||
$valid = md5(variable_get('uc_2checkout_secret_word', 'tango') . $_REQUEST['sid'] . $order_number . $_REQUEST['total']);
|
||||
if (drupal_strtolower($key) != drupal_strtolower($valid)) {
|
||||
uc_order_comment_save($order->order_id, 0, t('Attempted unverified 2Checkout completion for this order.'), 'admin');
|
||||
return MENU_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if ($_REQUEST['demo'] == 'Y' xor variable_get('uc_2checkout_demo', TRUE)) {
|
||||
watchdog('uc_2checkout', 'The 2checkout payment for order <a href="@order_url">@order_id</a> demo flag was set to %flag, but the module is set to %mode mode.', array(
|
||||
'@order_url' => url('admin/store/orders/' . $order->order_id),
|
||||
'@order_id' => $order->order_id,
|
||||
'%flag' => $_REQUEST['demo'] == 'Y' ? 'Y' : 'N',
|
||||
'%mode' => variable_get('uc_2checkout_demo', TRUE) ? 'Y' : 'N',
|
||||
), WATCHDOG_ERROR);
|
||||
|
||||
if (!variable_get('uc_2checkout_demo', TRUE)) {
|
||||
return MENU_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
$order->billing_street1 = $_REQUEST['street_address'];
|
||||
$order->billing_street2 = $_REQUEST['street_address2'];
|
||||
$order->city = $_REQUEST['city'];
|
||||
$order->billing_postal_code = $_REQUEST['zip'];
|
||||
$order->billing_phone = $_REQUEST['phone'];
|
||||
|
||||
$zone_id = db_query("SELECT zone_id FROM {uc_zones} WHERE zone_code LIKE :code", array(':code' => $_REQUEST['state']))->fetchField();
|
||||
if (!empty($zone_id)) {
|
||||
$order->billing_zone = $zone_id;
|
||||
}
|
||||
|
||||
$country_id = db_query("SELECT country_id FROM {uc_countries} WHERE country_name LIKE :name", array(':name' => $_REQUEST['country']))->fetchField();
|
||||
if (!empty($country_id)) {
|
||||
$order->billing_country = $country_id;
|
||||
}
|
||||
|
||||
// Save changes to order without it's completion.
|
||||
uc_order_save($order);
|
||||
|
||||
if (drupal_strtolower($_REQUEST['email']) !== drupal_strtolower($order->primary_email)) {
|
||||
uc_order_comment_save($order->order_id, 0, t('Customer used a different e-mail address during payment: !email', array('!email' => check_plain($_REQUEST['email']))), 'admin');
|
||||
}
|
||||
|
||||
if ($_REQUEST['credit_card_processed'] == 'Y' && is_numeric($_REQUEST['total'])) {
|
||||
$comment = t('Paid by !type, 2Checkout.com order #!order.', array('!type' => $_REQUEST['pay_method'] == 'CC' ? t('credit card') : t('echeck'), '!order' => check_plain($_REQUEST['order_number'])));
|
||||
uc_payment_enter($order->order_id, '2checkout', $_REQUEST['total'], 0, NULL, $comment);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Your order will be processed as soon as your payment clears at 2Checkout.com.'));
|
||||
uc_order_comment_save($order->order_id, 0, t('!type payment is pending approval at 2Checkout.com.', array('!type' => $_REQUEST['pay_method'] == 'CC' ? t('Credit card') : t('eCheck'))), 'admin');
|
||||
}
|
||||
|
||||
// Empty that cart...
|
||||
uc_cart_empty($cart_id);
|
||||
|
||||
// Add a comment to let sales team know this came in through the site.
|
||||
uc_order_comment_save($order->order_id, 0, t('Order created through website.'), 'admin');
|
||||
|
||||
$build = uc_cart_complete_sale($order, variable_get('uc_new_customer_login', FALSE));
|
||||
|
||||
$page = variable_get('uc_cart_checkout_complete_page', '');
|
||||
|
||||
if (!empty($page)) {
|
||||
drupal_goto($page);
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Page callbacks for administrative recurring fee operation pages.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Displays a form to update a subscriptions's CC info.
|
||||
*
|
||||
* @see uc_authorizenet_arb_admin_update_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_authorizenet_arb_admin_update_form($form, &$form_state, $rfid) {
|
||||
$order = new stdClass();
|
||||
|
||||
$fee = uc_recurring_fee_load('user', $rfid);
|
||||
|
||||
$form['rfid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $rfid,
|
||||
);
|
||||
$form['description'] = array(
|
||||
'#markup' => '<div>' . t('Subscription ID: @subscription_id', array('@subscription_id' => $fee['data'])) . '</div>',
|
||||
);
|
||||
|
||||
$form['cc_data'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Credit card details'),
|
||||
'#theme' => 'uc_payment_method_credit_form',
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$form['cc_data'] += uc_payment_method_credit_form(array(), $order);
|
||||
unset($form['cc_data']['cc_policy']);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update'),
|
||||
'#suffix' => l(t('Cancel'), 'admin/store/orders/recurring'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_authorizenet_arb_admin_update_form().
|
||||
*
|
||||
* @see uc_authorizenet_arb_admin_update_form()
|
||||
*/
|
||||
function uc_authorizenet_arb_admin_update_form_submit($form, &$form_state) {
|
||||
$fee = uc_recurring_fee_load('user', $form_state['values']['rfid']);
|
||||
|
||||
$updates = array(
|
||||
'payment' => array(
|
||||
'creditCard' => array(
|
||||
'cardNumber' => $form_state['values']['cc_data']['cc_number'],
|
||||
'expirationDate' => $form_state['values']['cc_data']['cc_exp_year'] . '-' . $form_state['values']['cc_data']['cc_exp_month'],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$result = uc_authorizenet_arb_update($fee['data'], $updates, $fee['order_id']);
|
||||
|
||||
// If the update was successful...
|
||||
if ($result) {
|
||||
drupal_set_message(t('Subscription data updated at Authorize.Net.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Subscription update failed. See order admin comments for more details.'), 'error');
|
||||
}
|
||||
|
||||
$form_state['redirect'] = 'admin/store/orders/recurring';
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirm form for canceling a subscription.
|
||||
*
|
||||
* @see uc_authorizenet_arb_admin_cancel_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_authorizenet_arb_admin_cancel_form($form, &$form_state, $rfid) {
|
||||
$form['rfid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $rfid,
|
||||
);
|
||||
|
||||
return confirm_form($form, t('Are you sure you wish to cancel this subscription?'), 'admin/store/orders/recurring', NULL, t('Confirm'), t('Cancel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_authorizenet_arb_admin_cancel_form().
|
||||
*
|
||||
* @see uc_authorizenet_arb_admin_cancel_form()
|
||||
*/
|
||||
function uc_authorizenet_arb_admin_cancel_form_submit($form, &$form_state) {
|
||||
$fee = uc_recurring_fee_load('user', $form_state['values']['rfid']);
|
||||
|
||||
$result = uc_authorizenet_arb_cancel($fee['data'], $fee['order_id'], $fee);
|
||||
|
||||
// If the cancellation was successful...
|
||||
if ($result) {
|
||||
drupal_set_message(t('Subscription canceled through Authorize.Net.'));
|
||||
|
||||
// Set the fee's recurring charges to 0.
|
||||
uc_recurring_fee_cancel($fee['rfid']);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('Subscription cancellation failed. See order admin comments for more details.'), 'error');
|
||||
}
|
||||
|
||||
$form_state['redirect'] = 'admin/store/orders/recurring';
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Authorize.net module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows transaction data to be altered before sending to Authorize.net.
|
||||
*
|
||||
* @param $data
|
||||
* The transaction data as specified by the Authorize.net API.
|
||||
*/
|
||||
function hook_uc_authorizenet_transaction_alter(&$data) {
|
||||
$data['x_description'] = 'Custom Authorize.Net transaction description.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
@@ -0,0 +1,13 @@
|
||||
name = Authorize.net
|
||||
description = Processes payments using Authorize.net. Supports AIM and ARB.
|
||||
dependencies[] = uc_payment
|
||||
dependencies[] = uc_credit
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_authorizenet module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function uc_authorizenet_requirements($phase) {
|
||||
$t = get_t();
|
||||
|
||||
$has_curl = function_exists('curl_init');
|
||||
|
||||
$requirements['uc_authorizenet_curl'] = array(
|
||||
'title' => $t('cURL'),
|
||||
'value' => $has_curl ? $t('Enabled') : $t('Not found'),
|
||||
);
|
||||
if (!$has_curl) {
|
||||
$requirements['uc_authorizenet_curl']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['uc_authorizenet_curl']['description'] = $t("Authorize.net requires the PHP <a href='!curl_url'>cURL</a> library.", array('!curl_url' => 'http://php.net/manual/en/curl.setup.php'));
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_authorizenet_uninstall() {
|
||||
// Delete related variables all at once.
|
||||
db_delete('variable')
|
||||
->condition('name', 'uc_authnet_%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_authorizenet_update_last_removed() {
|
||||
return 3;
|
||||
}
|
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Page callbacks for Authorize.Net's Silent POST feature and user
|
||||
* specific recurring fee operation pages.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Receives a payment notification and handles it appropriately.
|
||||
*/
|
||||
function uc_authorizenet_silent_post() {
|
||||
// Determine if this is an ARB notification or not
|
||||
$arb = (isset($_POST['x_subscription_id']) and isset($_POST['x_subscription_paynum']));
|
||||
|
||||
// Log ARB payment notification, if enabled.
|
||||
if (variable_get('uc_authnet_report_arb_post', FALSE)) {
|
||||
$args = array(
|
||||
'!arb' => $arb ? 'ARB ' : '',
|
||||
'@order_id' => $_POST['x_invoice_num'],
|
||||
'@post' => print_r($_POST, TRUE),
|
||||
);
|
||||
watchdog('uc_authorizenet', '!arbSilent POST received for order @order_id: <pre>@post</pre>', $args);
|
||||
}
|
||||
|
||||
// Decrypt the Auth.Net API login data.
|
||||
$login_data = _uc_authorizenet_login_data();
|
||||
|
||||
// TODO: Modify the MD5 hash to accommodate differences from AIM to ARB.
|
||||
|
||||
// This is an ARB notification.
|
||||
if ($arb) {
|
||||
|
||||
// Compare our expected MD5 Hash against what was received.
|
||||
$md5 = strtoupper(md5($login_data['md5_hash'] . $_POST['x_trans_id'] . $_POST['x_amount']));
|
||||
|
||||
// Post an error message if the MD5 hash does not validate.
|
||||
if ($_POST['x_MD5_Hash'] != $md5) {
|
||||
watchdog('uc_authorizenet', 'Invalid ARB payment notification received.', array(), WATCHDOG_ERROR);
|
||||
}
|
||||
// Otherwise, let other modules act on the data.
|
||||
else {
|
||||
module_invoke_all('uc_auth_arb_payment', $_POST);
|
||||
}
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a form for customers to update their CC info.
|
||||
*
|
||||
* @see uc_authorizenet_arb_user_update_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_authorizenet_arb_user_update_form($form, &$form_state, $user, $rfid) {
|
||||
$fee = uc_recurring_fee_load('user', $rfid);
|
||||
|
||||
$form['uid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $user->uid,
|
||||
);
|
||||
$form['rfid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $rfid,
|
||||
);
|
||||
$form['description'] = array(
|
||||
'#markup' => '<div>' . t('Recurring fee order ID: @order_id', array('@order_id' => $fee['order_id'])) . '</div>',
|
||||
);
|
||||
|
||||
$form['cc_data'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Credit card details'),
|
||||
'#theme' => 'uc_payment_method_credit_form',
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$form['cc_data'] += uc_payment_method_credit_form(array(), $order);
|
||||
unset($form['cc_data']['cc_policy']);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update'),
|
||||
'#suffix' => l(t('Cancel'), 'user/' . $user->uid),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_authorizenet_arb_user_update_form().
|
||||
*
|
||||
* @see uc_authorizenet_arb_user_update_form()
|
||||
*/
|
||||
function uc_authorizenet_arb_user_update_form_submit($form, &$form_state) {
|
||||
$fee = uc_recurring_fee_load('user', $form_state['values']['rfid']);
|
||||
|
||||
$updates = array(
|
||||
'payment' => array(
|
||||
'creditCard' => array(
|
||||
'cardNumber' => $form_state['values']['cc_data']['cc_number'],
|
||||
'expirationDate' => $form_state['values']['cc_data']['cc_exp_year'] . '-' . $form_state['values']['cc_data']['cc_exp_month'],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$result = uc_authorizenet_arb_update($fee['data'], $updates, $fee['order_id']);
|
||||
|
||||
// If the update was successful...
|
||||
if ($result) {
|
||||
drupal_set_message(t('The payment details for that recurring fee have been updated.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('An error has occurred while updating your payment details. Please try again and contact us if you are unable to perform the update.'), 'error');
|
||||
}
|
||||
|
||||
$form_state['redirect'] = 'user/' . $form_state['values']['uid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a confirm form for customers to cancel their fees.
|
||||
*
|
||||
* @see uc_authorizenet_arb_user_cancel_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_authorizenet_arb_user_cancel_form($form, &$form_state, $user, $rfid) {
|
||||
$form['uid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $user->uid,
|
||||
);
|
||||
$form['rfid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $rfid,
|
||||
);
|
||||
|
||||
return confirm_form($form, t('Are you sure you wish to cancel this fee?'), 'user/' . $user->uid, t('This action cannot be undone and may result in the termination of subscription services.'), t('Confirm'), t('Cancel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_authorizenet_arb_user_cancel_form().
|
||||
*
|
||||
* @see uc_authorizenet_arb_user_cancel_form()
|
||||
*/
|
||||
function uc_authorizenet_arb_user_cancel_form_submit($form, &$form_state) {
|
||||
$fee = uc_recurring_fee_load('user', $form_state['values']['rfid']);
|
||||
|
||||
$result = uc_authorizenet_arb_cancel($fee['data'], $fee['order_id'], $fee);
|
||||
|
||||
// If the cancellation was successful...
|
||||
if ($result) {
|
||||
drupal_set_message(t('The recurring fee has been canceled.'));
|
||||
|
||||
// Set the fee's recurring charges to 0.
|
||||
uc_recurring_fee_cancel($fee['rfid']);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('An error has occurred. Please try again and contact us if the problem persists.'), 'error');
|
||||
}
|
||||
|
||||
$form_state['redirect'] = 'user/' . $form_state['values']['uid'];
|
||||
}
|
After Width: | Height: | Size: 645 B |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 410 B |
After Width: | Height: | Size: 933 B |
After Width: | Height: | Size: 808 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 6.8 KiB |
@@ -0,0 +1,13 @@
|
||||
name = Test gateway
|
||||
description = Adds a credit card gateway that simulates a successful payment for testing checkout.
|
||||
dependencies[] = uc_payment
|
||||
dependencies[] = uc_credit
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* A dummy payment gateway to use for testing or as an example.
|
||||
*
|
||||
* All payments using this test gateway will succeed, except when:
|
||||
* - Credit card number equals '0000000000000000'. (Note that ANY card number
|
||||
* that fails the Luhn algorithm check performed by uc_credit will not even be
|
||||
* submitted to this gateway).
|
||||
* - CVV equals '000'.
|
||||
* - Credit card is expired.
|
||||
* - Payment amount equals 12.34 in store currency units.
|
||||
* - Customer's billing first name equals 'Fictitious'.
|
||||
* - Customer's billing telephone number equals '8675309'.
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Hook Functions (Ubercart)
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Implements hook_uc_payment_gateway().
|
||||
*
|
||||
* @see test_gateway.module
|
||||
* @see test_gateway_charge()
|
||||
*/
|
||||
function test_gateway_uc_payment_gateway() {
|
||||
$gateways['test_gateway'] = array(
|
||||
'title' => t('Test Gateway'),
|
||||
'description' => t('Process credit card payments through the Test Gateway.'),
|
||||
'credit' => 'test_gateway_charge',
|
||||
);
|
||||
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Module and Helper Functions
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Callback function to perform the charge operation.
|
||||
*
|
||||
* @see test_gateway.module
|
||||
* @see test_gateway_uc_payment_gateway()
|
||||
*/
|
||||
function test_gateway_charge($order_id, $amount, $data) {
|
||||
global $user;
|
||||
$order = uc_order_load($order_id);
|
||||
|
||||
// cc_exp_month and cc_exp_year are also validated by
|
||||
// _uc_credit_valid_card_expiration() on the checkout form.
|
||||
$month = $order->payment_details['cc_exp_month'];
|
||||
$year = $order->payment_details['cc_exp_year'];
|
||||
if ($year < 100) {
|
||||
$year = $year + 2000;
|
||||
}
|
||||
|
||||
// Card is expired at 0:00 on the first day of the next month.
|
||||
$expiration_date = mktime(0, 0, 0, $month + 1, 1, $year);
|
||||
|
||||
// Conditions for failure are described in file documentation block above.
|
||||
// All other transactions will succeed.
|
||||
if ($order->payment_details['cc_number'] == '0000000000000000' ||
|
||||
(isset($order->payment_details['cc_cvv']) && $order->payment_details['cc_cvv'] == '000') ||
|
||||
($expiration_date - REQUEST_TIME) <= 0 ||
|
||||
$amount == 12.34 ||
|
||||
$order->billing_first_name == 'Fictitious' ||
|
||||
$order->billing_phone == '8675309' ) {
|
||||
$success = FALSE;
|
||||
}
|
||||
else {
|
||||
$success = TRUE;
|
||||
}
|
||||
|
||||
// Uncomment this line to see the order object. The information for the
|
||||
// payment is in the $order->payment_details array.
|
||||
// drupal_set_message('<pre>' . print_r($order->payment_details, TRUE) . '</pre>');
|
||||
|
||||
if ($success) {
|
||||
$message = t('Credit card charged: !amount', array('!amount' => uc_currency_format($amount)));
|
||||
uc_order_comment_save($order_id, $user->uid, $message, 'admin');
|
||||
}
|
||||
else {
|
||||
$message = t('Credit card charge failed.');
|
||||
uc_order_comment_save($order_id, $user->uid, $message, 'admin');
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'success' => $success,
|
||||
'comment' => t('Card charged, resolution code: 0022548315'),
|
||||
'message' => $success ? t('Credit card payment processed successfully.') : t('Credit card charge failed.'),
|
||||
'uid' => $user->uid,
|
||||
// 'data' => $data,
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Credit card payment method tests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests credit card payments with the test gateway.
|
||||
*
|
||||
* This class is intended to be subclassed for use in testing other credit
|
||||
* card gateways. Subclasses which test other gateways need to:
|
||||
* - Define getInfo().
|
||||
* - Override setUp(), if necessary, to enable the other gateway and any other
|
||||
* needed modules.
|
||||
* - Override configureGateway() to implement gateway-specific configuration.
|
||||
* No other overrides are necessary, although a subclass may want to add
|
||||
* additional test functions to cover cases not included in this base class.
|
||||
*/
|
||||
class UbercartCreditCardTestCase extends UbercartTestHelper {
|
||||
|
||||
/**
|
||||
* A selection of "test" numbers to use for testing credit card payemnts.
|
||||
* These numbers all pass the Luhn algorithm check and are reserved by
|
||||
* the card issuer for testing purposes.
|
||||
*/
|
||||
protected static $test_cards = array(
|
||||
'378282246310005', // American Express
|
||||
'371449635398431',
|
||||
'370000000000002',
|
||||
'378734493671000', // American Express Corporate
|
||||
'5610591081018250', // Australian BankCard
|
||||
'30569309025904', // Diners Club
|
||||
'38520000023237',
|
||||
'38000000000006', // Carte Blanche
|
||||
'6011111111111117', // Discover
|
||||
'6011000990139424',
|
||||
'6011000000000012',
|
||||
'3530111333300000', // JCB
|
||||
'3566002020360505',
|
||||
'3088000000000017',
|
||||
'5555555555554444', // MasterCard
|
||||
'5105105105105100',
|
||||
'4111111111111111', // Visa
|
||||
'4012888888881881',
|
||||
'4007000000027',
|
||||
'4012888818888',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Describes this test case.
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Credit cards with Test Gateway',
|
||||
'description' => 'Uses the Test Gateway to ensure credit card processing is functioning.',
|
||||
'group' => 'Ubercart',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements DrupalWebTestCase::setUp().
|
||||
*/
|
||||
public function setUp(array $modules = array()) {
|
||||
$modules += array('uc_payment', 'uc_credit', 'test_gateway');
|
||||
$permissions = array('administer credit cards', 'process credit cards');
|
||||
parent::setUp($modules, $permissions);
|
||||
|
||||
// Need admin permissions in order to change credit card settings.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Configure and enable Credit card module and Test gateway.
|
||||
$this->configureCreditCard();
|
||||
$this->configureGateway();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to configure Credit Card payment method settings.
|
||||
*/
|
||||
protected function configureCreditCard() {
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment',
|
||||
array('uc_payment_method_credit_checkout' => TRUE),
|
||||
t('Save configuration')
|
||||
);
|
||||
$this->assertFieldByName(
|
||||
'uc_payment_method_credit_checkout',
|
||||
TRUE,
|
||||
t('Credit card payment method is enabled')
|
||||
);
|
||||
|
||||
// Create key directory, make it readable and writeable.
|
||||
// Putting this under sites/default/files because SimpleTest needs to be
|
||||
// able to create the directory - this is NOT where you'd put the key file
|
||||
// on a live site. On a live site, it should be outside the web root.
|
||||
drupal_mkdir('sites/default/files/simpletest.keys', 0755);
|
||||
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array(
|
||||
'uc_credit_encryption_path' => 'sites/default/files/simpletest.keys',
|
||||
),
|
||||
t('Save configuration')
|
||||
);
|
||||
|
||||
$this->assertFieldByName(
|
||||
'uc_credit_encryption_path',
|
||||
'sites/default/files/simpletest.keys',
|
||||
t('Key file path has been set.')
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
file_exists('sites/default/files/simpletest.keys/' . UC_CREDIT_KEYFILE_NAME),
|
||||
t('Key has been generated and stored.')
|
||||
);
|
||||
$this->pass('Key = ' . uc_credit_encryption_key());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to configure Credit Card gateway.
|
||||
*/
|
||||
protected function configureGateway() {
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array(
|
||||
'uc_payment_credit_gateway' => 'test_gateway',
|
||||
'uc_pg_test_gateway_enabled' => TRUE,
|
||||
),
|
||||
t('Save configuration')
|
||||
);
|
||||
|
||||
$this->assertFieldByName(
|
||||
'uc_pg_test_gateway_enabled',
|
||||
TRUE,
|
||||
t('Test gateway is enabled')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements DrupalWebTestCase::tearDown().
|
||||
*/
|
||||
public function tearDown() {
|
||||
// Cleanup keys directory after test.
|
||||
drupal_unlink('sites/default/files/simpletest.keys/' . UC_CREDIT_KEYFILE_NAME);
|
||||
drupal_rmdir('sites/default/files/simpletest.keys');
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function. Creates a new order.
|
||||
*/
|
||||
protected function createOrder($fields = array()) {
|
||||
$order = uc_order_new();
|
||||
foreach ($fields as $key => $value) {
|
||||
$order->$key = $value;
|
||||
}
|
||||
|
||||
if (empty($order->primary_email)) {
|
||||
$order->primary_email = $this->randomString() . '@example.org';
|
||||
}
|
||||
|
||||
if (!isset($fields['products'])) {
|
||||
$item = clone $this->product;
|
||||
$item->qty = 1;
|
||||
$item->price = $item->sell_price;
|
||||
$item->data = array();
|
||||
$order->products = array($item);
|
||||
}
|
||||
|
||||
$order->order_total = uc_order_get_total($order, TRUE);
|
||||
$order->line_items = uc_order_load_line_items($order, TRUE);
|
||||
uc_order_save($order);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests security settings configuration.
|
||||
*/
|
||||
public function testSecuritySettings() {
|
||||
// TODO: Still need tests with existing key file
|
||||
// where key file is not readable or doesn't contain a valid key
|
||||
|
||||
// Create key directory, make it readable and writeable.
|
||||
drupal_mkdir('sites/default/files/testkey', 0755);
|
||||
|
||||
// Try to submit settings form without a key file path.
|
||||
// Save current variable, reset to its value when first installed.
|
||||
$temp_variable = variable_get('uc_credit_encryption_path', '');
|
||||
variable_set('uc_credit_encryption_path', '');
|
||||
|
||||
$this->drupalGet('admin/store/settings/payment/method/credit');
|
||||
$this->assertText(t('Credit card security settings must be configured in the security settings tab.'));
|
||||
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array(),
|
||||
t('Save configuration')
|
||||
);
|
||||
$this->assertFieldByName(
|
||||
'uc_credit_encryption_path',
|
||||
t('Not configured.'),
|
||||
t('Key file has not yet been configured.')
|
||||
);
|
||||
// Restore variable setting.
|
||||
variable_set('uc_credit_encryption_path', $temp_variable);
|
||||
|
||||
// Try to submit settings form with an empty key file path.
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array('uc_credit_encryption_path' => ''),
|
||||
t('Save configuration')
|
||||
);
|
||||
$this->assertText('Key path must be specified in security settings tab.');
|
||||
|
||||
// Specify non-existent directory
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array('uc_credit_encryption_path' => 'sites/default/ljkh/asdfasfaaaaa'),
|
||||
t('Save configuration')
|
||||
);
|
||||
$this->assertText('You have specified a non-existent directory.');
|
||||
|
||||
// Next, specify existing directory that's write protected.
|
||||
// Use /dev, as that should never be accessible.
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array('uc_credit_encryption_path' => '/dev'),
|
||||
t('Save configuration')
|
||||
);
|
||||
$this->assertText('Cannot write to directory, please verify the directory permissions.');
|
||||
|
||||
// Next, specify writeable directory, but with excess whitespace
|
||||
// and trailing /
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array('uc_credit_encryption_path' => ' sites/default/files/testkey/ '),
|
||||
t('Save configuration')
|
||||
);
|
||||
// See that the directory has been properly re-written to remove
|
||||
// whitespace and trailing /
|
||||
$this->assertFieldByName(
|
||||
'uc_credit_encryption_path',
|
||||
'sites/default/files/testkey',
|
||||
t('Key file path has been set.')
|
||||
);
|
||||
$this->assertText('Credit card encryption key file generated.');
|
||||
|
||||
// Check that warning about needing key file goes away.
|
||||
$this->assertNoText(t('Credit card security settings must be configured in the security settings tab.'));
|
||||
// Remove key file.
|
||||
drupal_unlink('sites/default/files/testkey/' . UC_CREDIT_KEYFILE_NAME);
|
||||
|
||||
// Finally, specify good directory
|
||||
$this->drupalPost(
|
||||
'admin/store/settings/payment/method/credit',
|
||||
array('uc_credit_encryption_path' => 'sites/default/files/testkey'),
|
||||
t('Save configuration')
|
||||
);
|
||||
$this->assertText('Credit card encryption key file generated.');
|
||||
|
||||
// Test contents - must contain 32-character hexadecimal string.
|
||||
$this->assertTrue(
|
||||
file_exists('sites/default/files/simpletest.keys/' . UC_CREDIT_KEYFILE_NAME),
|
||||
t('Key has been generated and stored.')
|
||||
);
|
||||
$this->assertTrue(
|
||||
preg_match("([0-9a-fA-F]{32})", uc_credit_encryption_key()),
|
||||
t('Valid key detected in key file.')
|
||||
);
|
||||
|
||||
// Cleanup keys directory after test.
|
||||
drupal_unlink('sites/default/files/testkey/' . UC_CREDIT_KEYFILE_NAME);
|
||||
drupal_rmdir('sites/default/files/testkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an order can be placed using the test gateway.
|
||||
*/
|
||||
public function testCheckout() {
|
||||
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
|
||||
$this->checkout(array(
|
||||
'panes[payment][details][cc_number]' => array_rand(array_flip(self::$test_cards)),
|
||||
'panes[payment][details][cc_cvv]' => mt_rand(100, 999),
|
||||
'panes[payment][details][cc_exp_month]' => mt_rand(1, 12),
|
||||
'panes[payment][details][cc_exp_year]' => mt_rand(date('Y') + 1, 2022),
|
||||
));
|
||||
$this->assertText('Your order is complete!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that expiry date validation functions correctly.
|
||||
*/
|
||||
public function testExpiryDate() {
|
||||
$order = $this->createOrder(array('payment_method' => 'credit'));
|
||||
|
||||
$year = date('Y');
|
||||
$month = date('n');
|
||||
for ($y = $year; $y <= $year + 2; $y++) {
|
||||
for ($m = 1; $m <= 12; $m++) {
|
||||
$edit = array(
|
||||
'amount' => 1,
|
||||
'cc_data[cc_number]' => '4111111111111111',
|
||||
'cc_data[cc_cvv]' => '123',
|
||||
'cc_data[cc_exp_month]' => $m,
|
||||
'cc_data[cc_exp_year]' => $y,
|
||||
);
|
||||
$this->drupalPost('admin/store/orders/' . $order->order_id . '/credit', $edit, 'Charge amount');
|
||||
|
||||
if ($y > $year || $m >= $month) {
|
||||
$this->assertText('The credit card was processed successfully.', t('Card with expiry date @month/@year passed validation.', array('@month' => $m, '@year' => $y)));
|
||||
}
|
||||
else {
|
||||
$this->assertNoText('The credit card was processed successfully.', t('Card with expiry date @month/@year correctly failed validation.', array('@month' => $m, '@year' => $y)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,644 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Credit administration menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Credit card settings form.
|
||||
*
|
||||
* @see uc_credit_settings_form_validate()
|
||||
* @see uc_credit_settings_form_submit()
|
||||
*/
|
||||
function uc_credit_settings_form($form, &$form_state) {
|
||||
if (!user_access('administer credit cards')) {
|
||||
$form['notice'] = array(
|
||||
'#markup' => '<div>' . t('You must have access to <b>administer credit cards</b> to adjust these settings.') . '</div>',
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
$gateways = _uc_payment_gateway_list('credit');
|
||||
if (!count($gateways)) {
|
||||
$form['notice'] = array(
|
||||
'#markup' => '<div>' . t('Please enable a credit card gateway module for your chosen payment provider.') . '</div>',
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
$form['uc_credit'] = array(
|
||||
'#type' => 'vertical_tabs',
|
||||
'#attached' => array(
|
||||
'js' => array(
|
||||
'vertical-tabs' => drupal_get_path('module', 'uc_credit') . '/uc_credit.admin.js',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$form['cc_basic'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Basic settings'),
|
||||
'#group' => 'uc_credit',
|
||||
);
|
||||
$options = array();
|
||||
foreach ($gateways as $id => $gateway) {
|
||||
$options[$id] = $gateway['title'];
|
||||
}
|
||||
$form['cc_basic']['uc_payment_credit_gateway'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Default gateway'),
|
||||
'#options' => $options,
|
||||
'#default_value' => uc_credit_default_gateway(),
|
||||
);
|
||||
$form['cc_basic']['uc_credit_validate_numbers'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Validate credit card numbers at checkout.'),
|
||||
'#description' => t('Invalid card numbers will show an error message to the user so they can correct it.'),
|
||||
'#default_value' => variable_get('uc_credit_validate_numbers', TRUE),
|
||||
);
|
||||
|
||||
// Form elements that deal specifically with card number security.
|
||||
$form['cc_security'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Security settings'),
|
||||
'#description' => t('You are responsible for the security of your website, including the protection of credit card numbers. Please be aware that choosing some settings in this section may decrease the security of credit card data on your website and increase your liability for damages in the case of fraud.'),
|
||||
'#group' => 'uc_credit',
|
||||
);
|
||||
$form['cc_security']['uc_credit_encryption_path'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Encryption key directory'),
|
||||
'#description' => t('The card type, expiration date and last four digits of the card number are encrypted and stored temporarily while the customer is in the process of checking out.<br /><b>You must enable encryption</b> by following the <a href="!url">encryption instructions</a> in order to accept credit card payments.<br />In short, you must enter the path of a directory outside of your document root where the encryption key may be stored.<br />Relative paths will be resolved relative to the Drupal installation directory.<br />Once this directory is set, you should not change it.', array('!url' => 'http://drupal.org/node/1309226')),
|
||||
'#default_value' => uc_credit_encryption_key() ? variable_get('uc_credit_encryption_path', t('Not configured.')) : t('Not configured.'),
|
||||
);
|
||||
|
||||
// Form elements that deal with the type of data requested at checkout.
|
||||
$form['cc_fields'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Credit card fields'),
|
||||
'#description' => t('Specify what information to collect from customers in addition to the card number.'),
|
||||
'#group' => 'uc_credit',
|
||||
'#weight' => 10,
|
||||
);
|
||||
$form['cc_fields']['uc_credit_cvv_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable CVV text field on checkout form.'),
|
||||
'#description' => t('The CVV is an added security measure on credit cards. On Visa, Mastercard, and Discover cards it is a three digit number, and on AmEx cards it is a four digit number. If your credit card processor or payment gateway requires this information, you should enable this feature here.'),
|
||||
'#default_value' => variable_get('uc_credit_cvv_enabled', TRUE),
|
||||
);
|
||||
$form['cc_fields']['uc_credit_owner_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable card owner text field on checkout form.'),
|
||||
'#default_value' => variable_get('uc_credit_owner_enabled', FALSE),
|
||||
);
|
||||
$form['cc_fields']['uc_credit_start_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable card start date on checkout form.'),
|
||||
'#default_value' => variable_get('uc_credit_start_enabled', FALSE),
|
||||
);
|
||||
$form['cc_fields']['uc_credit_issue_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable card issue number text field on checkout form.'),
|
||||
'#default_value' => variable_get('uc_credit_issue_enabled', FALSE),
|
||||
);
|
||||
$form['cc_fields']['uc_credit_bank_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable issuing bank text field on checkout form.'),
|
||||
'#default_value' => variable_get('uc_credit_bank_enabled', FALSE),
|
||||
);
|
||||
$form['cc_fields']['uc_credit_type_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable card type selection on checkout form.'),
|
||||
'#description' => t('If enabled, specify in the textarea below which card options to populate the select box with.'),
|
||||
'#default_value' => variable_get('uc_credit_type_enabled', FALSE),
|
||||
);
|
||||
$form['cc_fields']['uc_credit_accepted_types'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Card type select box options'),
|
||||
'#description' => t('Enter one card type per line. These fields will populate the card type select box if it is enabled.'),
|
||||
'#default_value' => variable_get('uc_credit_accepted_types', implode("\r\n", array(t('Visa'), t('Mastercard'), t('Discover'), t('American Express')))),
|
||||
);
|
||||
|
||||
// Form elements that deal with card types accepted.
|
||||
$form['cc_fields']['cc_types'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Card types'),
|
||||
'#description' => t('Use the checkboxes to specify which card types you accept for payment. Selected card types will show their icons in the payment method selection list and be used for card number validation.'),
|
||||
);
|
||||
$form['cc_fields']['cc_types']['uc_credit_visa'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Visa'),
|
||||
'#default_value' => variable_get('uc_credit_visa', TRUE),
|
||||
);
|
||||
$form['cc_fields']['cc_types']['uc_credit_mastercard'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Mastercard'),
|
||||
'#default_value' => variable_get('uc_credit_mastercard', TRUE),
|
||||
);
|
||||
$form['cc_fields']['cc_types']['uc_credit_discover'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Discover'),
|
||||
'#default_value' => variable_get('uc_credit_discover', TRUE),
|
||||
);
|
||||
$form['cc_fields']['cc_types']['uc_credit_amex'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('American Express'),
|
||||
'#default_value' => variable_get('uc_credit_amex', TRUE),
|
||||
);
|
||||
|
||||
// Form elements that deal with credit card messages to customers.
|
||||
$form['cc_messages'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Customer messages'),
|
||||
'#description' => t('Here you can alter messages displayed to customers using credit cards.'),
|
||||
'#collapsible' => FALSE,
|
||||
'#group' => 'uc_credit',
|
||||
'#weight' => 10,
|
||||
);
|
||||
$form['cc_messages']['uc_credit_policy'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Credit card payment policy'),
|
||||
'#description' => t('Instructions for customers on the checkout page above the credit card fields.'),
|
||||
'#default_value' => variable_get('uc_credit_policy', t('Your billing information must match the billing address for the credit card entered below or we will be unable to process your payment.')),
|
||||
'#rows' => 3,
|
||||
);
|
||||
$form['cc_messages']['uc_credit_fail_message'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Card processing failure message'),
|
||||
'#description' => t('Error message displayed to customers when an attempted payment fails at checkout.'),
|
||||
'#default_value' => variable_get('uc_credit_fail_message', t('We were unable to process your credit card payment. Please verify your details and try again. If the problem persists, contact us to complete your order.')),
|
||||
);
|
||||
|
||||
$txn_types = array(
|
||||
UC_CREDIT_AUTH_ONLY => t('Authorization only'),
|
||||
UC_CREDIT_AUTH_CAPTURE => t('Authorize and capture immediately'),
|
||||
UC_CREDIT_REFERENCE_SET => t('Set a reference only'),
|
||||
);
|
||||
|
||||
foreach ($gateways as $id => $gateway) {
|
||||
$form['gateways'][$id] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => check_plain($gateway['title']),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'uc_credit',
|
||||
'#weight' => 5,
|
||||
);
|
||||
$form['gateways'][$id]['uc_pg_' . $id . '_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable this payment gateway for use.'),
|
||||
'#default_value' => variable_get('uc_pg_' . $id . '_enabled', TRUE),
|
||||
'#weight' => -10,
|
||||
);
|
||||
|
||||
// Get the transaction types associated with this gateway.
|
||||
$gateway_types = uc_credit_gateway_txn_types($id);
|
||||
$options = array();
|
||||
foreach ($txn_types as $type => $title) {
|
||||
if (in_array($type, $gateway_types)) {
|
||||
$options[$type] = $title;
|
||||
}
|
||||
}
|
||||
$form['gateways'][$id]['uc_pg_' . $id . '_cc_txn_type'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Default credit transaction type'),
|
||||
'#description' => t('Only available transaction types are listed. The default will be used unless an administrator chooses otherwise through the terminal.'),
|
||||
'#options' => $options,
|
||||
'#default_value' => variable_get('uc_pg_' . $id . '_cc_txn_type', UC_CREDIT_AUTH_CAPTURE),
|
||||
'#weight' => -5,
|
||||
);
|
||||
|
||||
if (isset($gateway['settings']) && function_exists($gateway['settings'])) {
|
||||
$gateway_settings = $gateway['settings'](array(), $form_state);
|
||||
if (is_array($gateway_settings)) {
|
||||
$form['gateways'][$id] += $gateway_settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($_POST) && !uc_credit_encryption_key()) {
|
||||
drupal_set_message(t('Credit card security settings must be configured in the security settings tab.'), 'warning');
|
||||
}
|
||||
|
||||
$form['#validate'][] = 'uc_credit_settings_form_validate';
|
||||
$form['#submit'][] = 'uc_credit_settings_form_submit';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the encryption key directory and key file.
|
||||
*
|
||||
* Checks that the encryption key directory has been specified, that it
|
||||
* exists, and is readable. and writeable so
|
||||
*
|
||||
* @see uc_credit_settings_form()
|
||||
* @see uc_credit_settings_form_submit()
|
||||
*/
|
||||
function uc_credit_settings_form_validate($form, &$form_state) {
|
||||
|
||||
// Trim trailing whitespace and any trailing / or \ from the key path name.
|
||||
$key_path = rtrim(trim($form_state['values']['uc_credit_encryption_path']), '/\\');
|
||||
|
||||
// Test to see if a path was entered.
|
||||
if (empty($key_path)) {
|
||||
form_set_error('uc_credit_encryption_path', t('Key path must be specified in security settings tab.'));
|
||||
}
|
||||
|
||||
// Construct complete key file path.
|
||||
$key_file = $key_path . '/' . UC_CREDIT_KEYFILE_NAME;
|
||||
|
||||
// Shortcut - test to see if we already have a usable key file.
|
||||
if (file_exists($key_file)) {
|
||||
if (is_readable($key_file)) {
|
||||
// Test contents - must contain 32-character hexadecimal string.
|
||||
$key = uc_credit_encryption_key();
|
||||
if ($key) {
|
||||
if (!preg_match("([0-9a-fA-F]{32})", $key)) {
|
||||
form_set_error('uc_credit_encryption_path', t('Key file already exists in directory, but it contains an invalid key.'));
|
||||
}
|
||||
else {
|
||||
// Key file exists and is valid, save result of trim() back into
|
||||
// $form_state and proceed to submit handler.
|
||||
$form_state['values']['uc_credit_encryption_path'] = $key_path;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
form_set_error('uc_credit_encryption_path', t('Key file already exists in directory, but is not readable. Please verify the file permissions.'));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if directory exists and is writeable.
|
||||
if (is_dir($key_path)) {
|
||||
// The entered directory is valid and in need of a key file.
|
||||
// Flag this condition for the submit handler.
|
||||
$form_state['uc_credit']['update_cc_encrypt_dir'] = TRUE;
|
||||
|
||||
// Can we open for writing?
|
||||
$file = @fopen($key_path . '/encrypt.test', 'w');
|
||||
if ($file === FALSE) {
|
||||
form_set_error('uc_credit_encryption_path', t('Cannot write to directory, please verify the directory permissions.'));
|
||||
unset($form_state['uc_credit']['update_cc_encrypt_dir']);
|
||||
}
|
||||
else {
|
||||
// Can we actually write?
|
||||
if (@fwrite($file, '0123456789') === FALSE) {
|
||||
form_set_error('uc_credit_encryption_path', t('Cannot write to directory, please verify the directory permissions.'));
|
||||
unset($form_state['uc_credit']['update_cc_encrypt_dir']);
|
||||
fclose($file);
|
||||
}
|
||||
else {
|
||||
// Can we read now?
|
||||
fclose($file);
|
||||
$file = @fopen($key_path . '/encrypt.test', 'r');
|
||||
if ($file === FALSE) {
|
||||
form_set_error('uc_credit_encryption_path', t('Cannot read from directory, please verify the directory permissions.'));
|
||||
unset($form_state['uc_credit']['update_cc_encrypt_dir']);
|
||||
}
|
||||
else {
|
||||
fclose($file);
|
||||
}
|
||||
}
|
||||
unlink($key_path . '/encrypt.test');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Directory doesn't exist.
|
||||
form_set_error('uc_credit_encryption_path', t('You have specified a non-existent directory.'));
|
||||
}
|
||||
|
||||
// If validation succeeds, save result of trim() back into $form_state.
|
||||
$form_state['values']['uc_credit_encryption_path'] = $key_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the encryption key file if it doesn't already exist.
|
||||
*
|
||||
* @see uc_credit_settings_form()
|
||||
* @see uc_credit_settings_form_validate()
|
||||
*/
|
||||
function uc_credit_settings_form_submit($form, &$form_state) {
|
||||
// Check to see if we need to create a key file.
|
||||
if (isset($form_state['uc_credit']['update_cc_encrypt_dir']) &&
|
||||
$form_state['uc_credit']['update_cc_encrypt_dir'] === TRUE) {
|
||||
|
||||
$key_path = $form_state['values']['uc_credit_encryption_path'];
|
||||
$key_file = $key_path . '/' . UC_CREDIT_KEYFILE_NAME;
|
||||
|
||||
if (!file_exists($key_file)) {
|
||||
if (!$file = fopen($key_file, 'wb')) {
|
||||
drupal_set_message(t('Credit card encryption key file creation failed for file @file. Check your filepath settings and directory permissions.', array('@file' => $key_file)), 'error');
|
||||
watchdog('uc_credit', 'Credit card encryption key file creation failed for file @file. Check your filepath settings and directory permissions.', array('@file' => $key_file), WATCHDOG_ERROR);
|
||||
}
|
||||
else {
|
||||
// Replacement key generation suggested by Barry Jaspan
|
||||
// for increased security.
|
||||
fwrite($file, md5(drupal_get_token(serialize($_REQUEST) . serialize($_SERVER) . REQUEST_TIME)));
|
||||
fclose($file);
|
||||
|
||||
drupal_set_message(t('Credit card encryption key file generated. Card data will now be encrypted.'));
|
||||
watchdog('uc_credit', 'Credit card encryption key file generated. Card data will now be encrypted.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the credit card terminal page.
|
||||
*/
|
||||
function uc_credit_terminal($order) {
|
||||
$build['details']['order_total'] = array('#markup' => '<div><strong>' . t('Order total: @total', array('@total' => uc_currency_format($order->order_total))) . '</strong></div>');
|
||||
$build['details']['balance'] = array('#markup' => '<div><strong>' . t('Balance: @balance', array('@balance' => uc_currency_format(uc_payment_balance($order)))) . '</strong></div>');
|
||||
|
||||
$build['form'] = drupal_get_form('uc_credit_terminal_form', $order);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the credit card terminal form for administrators.
|
||||
*
|
||||
* @see uc_credit_terminal_form_validate()
|
||||
* @see uc_credit_terminal_form_submit()
|
||||
*/
|
||||
function uc_credit_terminal_form($form, &$form_state, $order, $lock_amount = FALSE) {
|
||||
// Get the transaction types available to our default gateway.
|
||||
$types = uc_credit_gateway_txn_types(uc_credit_default_gateway());
|
||||
|
||||
// Put the order ID in the form.
|
||||
$form['order_id'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $order->order_id,
|
||||
);
|
||||
|
||||
$balance = uc_payment_balance($order);
|
||||
|
||||
// Let the administrator set the amount to charge.
|
||||
$form['amount'] = array(
|
||||
'#type' => 'uc_price',
|
||||
'#title' => t('Charge Amount'),
|
||||
'#default_value' => $balance > 0 ? uc_currency_format($balance, FALSE, FALSE, '.') : 0,
|
||||
'#disabled' => $lock_amount,
|
||||
);
|
||||
|
||||
// Build a credit card form.
|
||||
$form['specify_card'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Credit card details'),
|
||||
'#description' => t('Use the available buttons in this fieldset to process with the specified card details.'),
|
||||
);
|
||||
$form['specify_card']['cc_data'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#prefix' => '<div class="payment-details-credit clearfix">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$form['specify_card']['cc_data'] += uc_payment_method_credit_form(array(), $form_state, $order);
|
||||
unset($form['specify_card']['cc_data']['cc_policy']);
|
||||
|
||||
$form['specify_card']['actions'] = array('#type' => 'actions');
|
||||
|
||||
// If available, let the card be charged now.
|
||||
if (in_array(UC_CREDIT_AUTH_CAPTURE, $types)) {
|
||||
$form['specify_card']['actions']['charge_card'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Charge amount'),
|
||||
);
|
||||
}
|
||||
|
||||
// If available, let the amount be authorized.
|
||||
if (in_array(UC_CREDIT_AUTH_ONLY, $types)) {
|
||||
$form['specify_card']['actions']['authorize_card'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Authorize amount only'),
|
||||
);
|
||||
}
|
||||
|
||||
// If available, create a reference at the gateway.
|
||||
if (in_array(UC_CREDIT_REFERENCE_SET, $types)) {
|
||||
$form['specify_card']['actions']['reference_set'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Set a reference only'),
|
||||
);
|
||||
}
|
||||
|
||||
// If available, create a reference at the gateway.
|
||||
if (in_array(UC_CREDIT_CREDIT, $types)) {
|
||||
$form['specify_card']['actions']['credit_card'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Credit amount to this card'),
|
||||
);
|
||||
}
|
||||
|
||||
// Find any uncaptured authorizations.
|
||||
$options = array();
|
||||
|
||||
if (isset($order->data['cc_txns']['authorizations'])) {
|
||||
foreach ($order->data['cc_txns']['authorizations'] as $auth_id => $data) {
|
||||
if (empty($data['captured'])) {
|
||||
$options[$auth_id] = t('@auth_id - @date - @amount authorized', array('@auth_id' => strtoupper($auth_id), '@date' => format_date($data['authorized'], 'short'), '@amount' => uc_currency_format($data['amount'])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If any authorizations existed...
|
||||
if (!empty($options)) {
|
||||
// Display a fieldset with the authorizations and available action buttons.
|
||||
$form['authorizations'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Prior authorizations'),
|
||||
'#description' => t('Use the available buttons in this fieldset to select and act on a prior authorization. The charge amount specified above will be captured against the authorization listed below. Only one capture is possible per authorization, and a capture for more than the amount of the authorization may result in additional fees to you.'),
|
||||
);
|
||||
|
||||
$form['authorizations']['select_auth'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Select authorization'),
|
||||
'#options' => $options,
|
||||
);
|
||||
|
||||
$form['authorizations']['actions'] = array('#type' => 'actions');
|
||||
|
||||
// If available, capture a prior authorization.
|
||||
if (in_array(UC_CREDIT_PRIOR_AUTH_CAPTURE, $types)) {
|
||||
$form['authorizations']['actions']['auth_capture'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Capture amount to this authorization'),
|
||||
);
|
||||
}
|
||||
|
||||
// If available, void a prior authorization.
|
||||
if (in_array(UC_CREDIT_VOID, $types)) {
|
||||
$form['authorizations']['actions']['auth_void'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Void authorization'),
|
||||
);
|
||||
}
|
||||
|
||||
// Collapse this fieldset if no actions are available.
|
||||
if (!isset($form['authorizations']['actions']['auth_capture']) && !isset($form['authorizations']['actions']['auth_void'])) {
|
||||
$form['authorizations']['#collapsible'] = TRUE;
|
||||
$form['authorizations']['#collapsed'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Find any uncaptured authorizations.
|
||||
$options = array();
|
||||
|
||||
// Log a reference to the order for testing.
|
||||
// $order->data = uc_credit_log_reference($order->order_id, substr(md5(REQUEST_TIME), 0, 16), '4111111111111111');
|
||||
|
||||
if (isset($order->data['cc_txns']['references'])) {
|
||||
foreach ($order->data['cc_txns']['references'] as $ref_id => $data) {
|
||||
$options[$ref_id] = t('@ref_id - @date - (Last 4) @card', array('@ref_id' => strtoupper($ref_id), '@date' => format_date($data['created'], 'short'), '@card' => $data['card']));
|
||||
}
|
||||
}
|
||||
|
||||
// If any references existed...
|
||||
if (!empty($options)) {
|
||||
// Display a fieldset with the authorizations and available action buttons.
|
||||
$form['references'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Customer references'),
|
||||
'#description' => t('Use the available buttons in this fieldset to select and act on a customer reference.'),
|
||||
);
|
||||
|
||||
$form['references']['select_ref'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Select references'),
|
||||
'#options' => $options,
|
||||
);
|
||||
|
||||
$form['references']['actions'] = array('#type' => 'actions');
|
||||
|
||||
// If available, capture a prior references.
|
||||
if (in_array(UC_CREDIT_REFERENCE_TXN, $types)) {
|
||||
$form['references']['actions']['ref_capture'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Charge amount to this reference'),
|
||||
);
|
||||
}
|
||||
|
||||
// If available, remove a previously stored reference.
|
||||
if (in_array(UC_CREDIT_REFERENCE_REMOVE, $types)) {
|
||||
$form['references']['actions']['ref_remove'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Remove reference'),
|
||||
);
|
||||
}
|
||||
|
||||
// If available, remove a previously stored reference.
|
||||
if (in_array(UC_CREDIT_REFERENCE_CREDIT, $types)) {
|
||||
$form['references']['actions']['ref_credit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Credit amount to this reference'),
|
||||
);
|
||||
}
|
||||
|
||||
// Collapse this fieldset if no actions are available.
|
||||
if (!isset($form['references']['actions']['ref_capture']) && !isset($form['references']['actions']['ref_remove']) && !isset($form['references']['actions']['ref_credit'])) {
|
||||
$form['references']['#collapsible'] = TRUE;
|
||||
$form['references']['#collapsed'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
$form['#attached']['css'][] = drupal_get_path('module', 'uc_payment') . '/uc_payment.css';
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation handler for credit terminal form.
|
||||
*
|
||||
* @see uc_credit_terminal_form()
|
||||
* @see uc_credit_terminal_form_submit()
|
||||
*/
|
||||
function uc_credit_terminal_form_validate($form, &$form_state) {
|
||||
if (uc_order_load($form_state['values']['order_id']) === FALSE) {
|
||||
form_set_error('', t('Invalid order ID. Unable to process payment.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for credit terminal form.
|
||||
*
|
||||
* @see uc_credit_terminal_form()
|
||||
* @see uc_credit_terminal_form_validate()
|
||||
*/
|
||||
function uc_credit_terminal_form_submit($form, &$form_state) {
|
||||
// Load the order.
|
||||
$order = uc_order_load($form_state['values']['order_id']);
|
||||
|
||||
// Get the data from the form and replace masked data from the order.
|
||||
$cc_data = $form_state['values']['cc_data'];
|
||||
|
||||
if (strpos($cc_data['cc_number'], t('(Last 4) ')) === 0) {
|
||||
$cc_data['cc_number'] = $order->payment_details['cc_number'];
|
||||
}
|
||||
|
||||
if (isset($cc_data['cc_cvv']) && isset($order->payment_details['cc_cvv'])) {
|
||||
if ($cc_data['cc_cvv'] == str_repeat('-', strlen($cc_data['cc_cvv']))) {
|
||||
$cc_data['cc_cvv'] = $order->payment_details['cc_cvv'];
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the values for use during processing.
|
||||
uc_credit_cache('save', $cc_data, FALSE);
|
||||
|
||||
// Build the data array passed on to the payment gateway.
|
||||
$data = array();
|
||||
|
||||
switch ($form_state['values']['op']) {
|
||||
case t('Charge amount'):
|
||||
$data['txn_type'] = UC_CREDIT_AUTH_CAPTURE;
|
||||
break;
|
||||
|
||||
case t('Authorize amount only'):
|
||||
$data['txn_type'] = UC_CREDIT_AUTH_ONLY;
|
||||
break;
|
||||
|
||||
case t('Set a reference only'):
|
||||
$data['txn_type'] = UC_CREDIT_REFERENCE_SET;
|
||||
break;
|
||||
|
||||
case t('Credit amount to this card'):
|
||||
$data['txn_type'] = UC_CREDIT_CREDIT;
|
||||
break;
|
||||
|
||||
case t('Capture amount to this authorization'):
|
||||
$data['txn_type'] = UC_CREDIT_PRIOR_AUTH_CAPTURE;
|
||||
$data['auth_id'] = $form_state['values']['select_auth'];
|
||||
break;
|
||||
|
||||
case t('Void authorization'):
|
||||
$data['txn_type'] = UC_CREDIT_VOID;
|
||||
$data['auth_id'] = $form_state['values']['select_auth'];
|
||||
break;
|
||||
|
||||
case t('Charge amount to this reference'):
|
||||
$data['txn_type'] = UC_CREDIT_REFERENCE_TXN;
|
||||
$data['ref_id'] = $form_state['values']['select_ref'];
|
||||
break;
|
||||
|
||||
case t('Remove reference'):
|
||||
$data['txn_type'] = UC_CREDIT_REFERENCE_REMOVE;
|
||||
$data['ref_id'] = $form_state['values']['select_ref'];
|
||||
break;
|
||||
|
||||
case t('Credit amount to this reference'):
|
||||
$data['txn_type'] = UC_CREDIT_REFERENCE_CREDIT;
|
||||
$data['ref_id'] = $form_state['values']['select_ref'];
|
||||
}
|
||||
|
||||
$result = uc_payment_process_payment('credit', $form_state['values']['order_id'], $form_state['values']['amount'], $data, TRUE, NULL, FALSE);
|
||||
_uc_credit_save_cc_data_to_order(uc_credit_cache('load'), $form_state['values']['order_id']);
|
||||
|
||||
if ($result) {
|
||||
drupal_set_message(t('The credit card was processed successfully. See the admin comments for more details.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('There was an error processing the credit card. See the admin comments for details.'), 'error');
|
||||
}
|
||||
|
||||
$form_state['redirect'] = 'admin/store/orders/' . $form_state['values']['order_id'];
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @file
|
||||
* Utility functions to display settings summaries on vertical tabs.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.creditAdminFieldsetSummaries = {
|
||||
attach: function (context) {
|
||||
$('fieldset#edit-cc-security', context).drupalSetSummary(function(context) {
|
||||
return Drupal.t('Encryption key path') + ': '
|
||||
+ $('#edit-uc-credit-encryption-path', context).val();
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
@@ -0,0 +1,16 @@
|
||||
name = Credit card
|
||||
description = Enables support for credit card payments at checkout.
|
||||
dependencies[] = uc_store
|
||||
dependencies[] = uc_payment
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Test cases
|
||||
files[] = tests/uc_credit.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_credit module.
|
||||
*
|
||||
* This is mostly legacy code now.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_credit_uninstall() {
|
||||
db_delete('variable')
|
||||
->condition('name', 'uc_credit_%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_credit_update_last_removed() {
|
||||
// 7.x-3.0-beta2 and earlier were installed with schema version 0,
|
||||
// which causes update.php to fail.
|
||||
return drupal_get_installed_schema_version('uc_credit') == 0 ? 0 : 6000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove credit card debug mode.
|
||||
*/
|
||||
function uc_credit_update_7000(&$sandbox) {
|
||||
drupal_load('module', 'uc_credit');
|
||||
module_load_include('inc', 'uc_store', 'classes/encrypt');
|
||||
|
||||
if (!isset($sandbox['progress'])) {
|
||||
$sandbox['progress'] = 0;
|
||||
$sandbox['current_order_id'] = 0;
|
||||
$sandbox['max'] = db_query("SELECT COUNT(order_id) FROM {uc_orders} WHERE data LIKE '%cc_data%'")->fetchField();
|
||||
}
|
||||
|
||||
$t = get_t();
|
||||
$limit = 100;
|
||||
$crypt = new UbercartEncryption();
|
||||
$key = uc_credit_encryption_key();
|
||||
|
||||
// Truncate stored data from debug mode.
|
||||
$result = db_query_range("SELECT order_id, data FROM {uc_orders} WHERE order_id > :current AND data LIKE '%cc_data%' ORDER BY order_id", 0, $limit, array(':current' => $sandbox['current_order_id']));
|
||||
while ($order = $result->fetchObject()) {
|
||||
$sandbox['current_order_id'] = $order->order_id;
|
||||
|
||||
// Load up the existing data array.
|
||||
$data = unserialize($order->data);
|
||||
$cc_data = uc_credit_cache('save', $data['cc_data']);
|
||||
|
||||
// Save only some limited, PCI compliant data.
|
||||
$cc_data['cc_number'] = substr($cc_data['cc_number'], -4);
|
||||
unset($cc_data['cc_cvv']);
|
||||
|
||||
// Stuff the serialized and encrypted CC details into the array.
|
||||
$data['cc_data'] = $crypt->encrypt($key, base64_encode(serialize($cc_data)));
|
||||
|
||||
// Save it again.
|
||||
db_update('uc_orders')
|
||||
->fields(array('data' => serialize($data)))
|
||||
->condition('order_id', $order->order_id)
|
||||
->execute();
|
||||
|
||||
$sandbox['progress']++;
|
||||
$sandbox['message'] = $t('Scrubbed credit card data from order @order_id', array('@order_id' => $order->order_id));
|
||||
}
|
||||
|
||||
|
||||
if ($sandbox['progress'] < $sandbox['max']) {
|
||||
$sandbox['#finished'] = min(0.99, $sandbox['progress'] / $sandbox['max']);
|
||||
}
|
||||
else {
|
||||
$sandbox['#finished'] = 1;
|
||||
|
||||
variable_del('uc_credit_checkout_process');
|
||||
variable_del('uc_credit_debug');
|
||||
variable_del('uc_credit_clear_status');
|
||||
variable_del('uc_credit_number_duration');
|
||||
variable_del('uc_credit_number_unit');
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Credit menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prints the contents of the CVV information popup window.
|
||||
*/
|
||||
function uc_credit_cvv_info() {
|
||||
$output = "<!DOCTYPE html>\n";
|
||||
$output .= '<html><head><meta charset="utf-8" /></head><body>';
|
||||
|
||||
$output .= '<b>' . t('What is the CVV?') . '</b><p>' . t('CVV stands for Card Verification Value. This number is used as a security feature to protect you from credit card fraud. Finding the number on your card is a very simple process. Just follow the directions below.') . '</p>';
|
||||
$cc_types = array(
|
||||
'visa' => t('Visa'),
|
||||
'mastercard' => t('MasterCard'),
|
||||
'discover' => t('Discover')
|
||||
);
|
||||
foreach ($cc_types as $id => $type) {
|
||||
if (variable_get('uc_credit_' . $id, TRUE)) {
|
||||
$valid_types[] = $type;
|
||||
}
|
||||
}
|
||||
if (count($valid_types) > 0) {
|
||||
$output .= '<br /><b>' . implode(', ', $valid_types) . ':</b><p>';
|
||||
$output .= theme('image', array(
|
||||
'path' => drupal_get_path('module', 'uc_credit') . '/images/visa_cvv.jpg',
|
||||
'attributes' => array('align' => 'left'),
|
||||
));
|
||||
$output .= t('The CVV for these cards is found on the back side of the card. It is only the last three digits on the far right of the signature panel box.');
|
||||
$output .= '</p>';
|
||||
}
|
||||
|
||||
if (variable_get('uc_credit_amex', TRUE)) {
|
||||
$output .= '<br /><p><b>' . t('American Express') . ':</b><p>';
|
||||
$output .= theme('image', array(
|
||||
'path' => drupal_get_path('module', 'uc_credit') . '/images/amex_cvv.jpg',
|
||||
'attributes' => array('align' => 'left'),
|
||||
));
|
||||
$output .= t('The CVV on American Express cards is found on the front of the card. It is a four digit number printed in smaller text on the right side above the credit card number.');
|
||||
$output .= '</p>';
|
||||
}
|
||||
|
||||
$output .= '<p align="right"><input type="button" onclick="self.close();" value="' . t('Close this window') . '" /></p>';
|
||||
|
||||
$output .= '</body></html>';
|
||||
|
||||
print $output;
|
||||
exit();
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Theme functions for the uc_credit module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Themes the credit card CVV help link.
|
||||
*/
|
||||
function theme_uc_credit_cvv_help($variables) {
|
||||
$output = '<div class="uc-credit-cvv-help">';
|
||||
$output .= theme('image', array('path' => drupal_get_path('module', 'uc_credit') . '/images/info.png'));
|
||||
$output .= ' ' . l(t("What's the CVV?"), 'cart/checkout/credit/cvv_info', array('attributes' => array(
|
||||
'onclick' => "window.open(this.href, 'CVV_Info', 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=1,width=480,height=460'); return false;",
|
||||
)));
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
name = CyberSource
|
||||
description = Processes payments using the CyberSource Silent Order POST and Hosted Order Page services.
|
||||
dependencies[] = uc_payment
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Class definitions
|
||||
files[] = uc_cybersource.soap.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_cybersource module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function uc_cybersource_requirements($phase) {
|
||||
$t = get_t();
|
||||
|
||||
$has_curl = function_exists('curl_init');
|
||||
$has_dom = class_exists('DOMDocument');
|
||||
$has_soap = class_exists('SoapClient');
|
||||
|
||||
// Valid methods are 'soap' and 'post'. Defaults here to 'post' on install.
|
||||
$method = variable_get('uc_cybersource_method', 'post');
|
||||
|
||||
// Using SOAP.
|
||||
if ($method == 'soap') {
|
||||
$requirements['uc_cybersource_soap_and_dom'] = array(
|
||||
'title' => $t('SOAP and DOM'),
|
||||
'value' => $has_soap && $has_dom ? $t('Enabled') : $t('Not found'),
|
||||
);
|
||||
if (!$has_soap || !$has_dom) {
|
||||
$requirements['uc_cybersource_soap_and_dom']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['uc_cybersource_soap_and_dom']['description'] = $t("CyberSource's SOAP Toolkit API requires the PHP <a href='!soap_url'>SOAP</a> and <a href='!dom_url'>DOM</a> libraries.", array('!soap_url' => 'http://php.net/manual/en/soap.setup.php', '!dom_url' => 'http://php.net/manual/en/dom.setup.php'));
|
||||
}
|
||||
}
|
||||
|
||||
// Using POST with cURL.
|
||||
elseif ($method == 'post') {
|
||||
$requirements['uc_cybersource_curl'] = array(
|
||||
'title' => $t('cURL'),
|
||||
'value' => $has_curl ? $t('Enabled') : $t('Not found'),
|
||||
);
|
||||
if (!$has_curl) {
|
||||
$requirements['uc_cybersource_curl']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['uc_cybersource_curl']['description'] = $t("CyberSource's Silent Order POST requires the PHP <a href='!curl_url'>cURL</a> library.", array('!curl_url' => 'http://php.net/manual/en/curl.setup.php'));
|
||||
}
|
||||
}
|
||||
|
||||
// We need HOP.php if we're using the post method and also the Hosted Order
|
||||
// page, which is a payment method rather than a gateway.
|
||||
if ($method != 'soap') {
|
||||
require_once('uc_cybersource.module');
|
||||
$has_cybersource_hop = uc_cybersource_hop_include();
|
||||
$requirements['uc_cybersource'] = array(
|
||||
'title' => t('CyberSource HOP.php'),
|
||||
'value' => $has_cybersource_hop ? $t('Enabled') : $t('Not found'),
|
||||
);
|
||||
if (!$has_cybersource_hop) {
|
||||
$requirements['uc_cybersource']['description'] = t('For instructions on how to generate this file, please refer to <a href="http://www.cybersource.com/support_center/implementation/downloads/hosted_order_page/">CyberSource\'s Hosted Order Pages Documentation</a>, specifically the "Downloading Security Scripts" in Chapter 2 of the <a href="http://apps.cybersource.com/library/documentation/sbc/HOP_UG/html/">HOP User\'s Guide</a>.');
|
||||
$severity = $phase == 'install' ? REQUIREMENT_INFO : REQUIREMENT_ERROR;
|
||||
$requirements['uc_cybersource']['severity'] = $severity;
|
||||
}
|
||||
else {
|
||||
$requirements['uc_cybersource']['severity'] = REQUIREMENT_OK;
|
||||
$requirements['uc_cybersource']['description'] = t('The HOP.php library is readable.');
|
||||
}
|
||||
}
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_cybersource_uninstall() {
|
||||
// Delete related variables all at once.
|
||||
db_delete('variable')
|
||||
->condition(db_or()
|
||||
->condition('name', 'uc_cybersource_%', 'LIKE')
|
||||
->condition('name', 'cs_ship_from_%', 'LIKE')
|
||||
)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_cybersource_schema() {
|
||||
$schema['uc_payment_cybersource_hop_post'] = array(
|
||||
'fields' => array(
|
||||
'order_id' => array(
|
||||
'description' => 'The order ID as provided by the store.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'request_id' => array(
|
||||
'description' => 'Unique id assigned by CyberSource that identifies payment request.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'request_token' => array(
|
||||
'description' => 'Verification token associated with the request ID.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'reconciliation_id' => array(
|
||||
'description' => 'Reference number generated by CyberSource that you use to reconcile your CyberSource reports with your processor reports.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'gross' => array(
|
||||
'description' => 'The approved payment amount from CyberSource.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'decision' => array(
|
||||
'description' => 'CyberSource decision for payment request.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'reason_code' => array(
|
||||
'description' => 'A code for decision.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'payer_email' => array(
|
||||
'description' => 'The e-mail address of the buyer.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'received' => array(
|
||||
'description' => 'The IPN receipt timestamp.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_cybersource_update_last_removed() {
|
||||
return 2;
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Defines a class used for communicating with CyberSource via SOAP.
|
||||
*
|
||||
* Provided by Acquia, Commercially supported Drupal - http://acquia.com
|
||||
*/
|
||||
|
||||
class CyberSourceSoapClient extends SoapClient {
|
||||
function __construct($wsdl, $options = NULL) {
|
||||
parent::__construct($wsdl, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the UsernameToken information in the outgoing request.
|
||||
*/
|
||||
function __doRequest($request, $location, $action, $version) {
|
||||
$login = _uc_cybersource_soap_login_data();
|
||||
|
||||
$soapHeader = '<SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsse:Security SOAP-ENV:mustUnderstand="1"><wsse:UsernameToken><wsse:Username>' . $login['merchant_id'] . '</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">' . $login['transaction_key'] . '</wsse:Password></wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>';
|
||||
|
||||
$requestDOM = new DOMDocument('1.0');
|
||||
$soapHeaderDOM = new DOMDocument('1.0');
|
||||
|
||||
try {
|
||||
$requestDOM->loadXML($request);
|
||||
$soapHeaderDOM->loadXML($soapHeader);
|
||||
$node = $requestDOM->importNode($soapHeaderDOM->firstChild, TRUE);
|
||||
$requestDOM->firstChild->insertBefore($node, $requestDOM->firstChild->firstChild);
|
||||
$request = $requestDOM->saveXML();
|
||||
}
|
||||
catch (DOMException $e) {
|
||||
die('Error adding UsernameToken: ' . $e->code);
|
||||
}
|
||||
|
||||
return parent::__doRequest($request, $location, $action, $version);
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
The Google Checkout module has been removed from Ubercart.
|
||||
|
||||
As per https://support.google.com/checkout/sell/answer/3080449,
|
||||
Google Checkout is no longer in operation as of 20 November 2013 and
|
||||
can no longer be used as a payment method. Therefore, we've removed
|
||||
the uc_google_checkout module entirely.
|
||||
|
||||
A skeleton module has been left in place to allow you to UNINSTALL
|
||||
uc_google_checkout from any site where you had it running. Be sure
|
||||
to follow the three steps in https://drupal.org/node/2145671 to
|
||||
completely uninstall the uc_google_checkout module from your site.
|
||||
You may delete the ubercart/uc_google_checkout directory and all its
|
||||
contents after you competely uninstall it.
|
@@ -0,0 +1,14 @@
|
||||
name = Google Checkout
|
||||
description = This module is obsolete and CANNOT be installed. Uninstall hooks remain so you can remove this module if you previously had it installed.
|
||||
dependencies[] = uc_cart
|
||||
dependencies[] = uc_payment
|
||||
dependencies[] = uc_quote
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_google_checkout module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_google_checkout_uninstall() {
|
||||
db_update('uc_order_statuses')
|
||||
->fields(array(
|
||||
'locked' => 0,
|
||||
))
|
||||
->condition(db_or()
|
||||
->condition('order_status_id', 'in_google_checkout')
|
||||
->condition('order_status_id', 'chargeable')
|
||||
)
|
||||
->execute();
|
||||
|
||||
db_delete('variable')
|
||||
->condition('name', 'uc_google_checkout_%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function uc_google_checkout_requirements($phase) {
|
||||
$t = get_t();
|
||||
|
||||
$requirements['uc_google_checkout'] = array(
|
||||
'title' => $t('Google Checkout'),
|
||||
'description' => $t('Google Checkout is no longer in operation. This module cannot be installed, only uninstalled. If you already have it installed, you must uninstall it. Follow the instructions in the module\'s <a href="@readme">README.txt</a>', array('@readme' => '/' . drupal_get_path('module', 'uc_google_checkout') . '/README.txt')),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
);
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_google_checkout_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_gc_products'] = array(
|
||||
'description' => 'Stores Google Checkout information for products.',
|
||||
'fields' => array(
|
||||
'vid' => array(
|
||||
'description' => 'The {uc_products}.vid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'nid' => array(
|
||||
'description' => 'The {uc_products}.nid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'gc_salable' => array(
|
||||
'description' => 'A flag indicating whether the product can be sold with Google Checkout. 1 => Yes. 0 => No.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
),
|
||||
'primary key' => array('vid'),
|
||||
'foreign keys' => array(
|
||||
'uc_products' => array(
|
||||
'table' => 'uc_products',
|
||||
'columns' => array(
|
||||
'nid' => 'nid',
|
||||
'vid' => 'vid',
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_gc_orders'] = array(
|
||||
'description' => 'Stores Google Checkout orders information.',
|
||||
'fields' => array(
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'gc_order_number' => array(
|
||||
'description' => 'The Google Checkout order number.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '0',
|
||||
),
|
||||
'financial_state' => array(
|
||||
'description' => 'The order financial state.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => 'REVIEWING',
|
||||
),
|
||||
'fulfillment_state' => array(
|
||||
'description' => 'The order fulfillment state.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => 'NEW',
|
||||
),
|
||||
'gc_total' => array(
|
||||
'description' => 'The order total according to Google Checkout.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => TRUE,
|
||||
'default' => 0.0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'gc_order_number' => array(array('gc_order_number', 20)),
|
||||
),
|
||||
'primary key' => array('order_id'),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_gc_taxes'] = array(
|
||||
'description' => 'Stores tax information for Google Checkout.',
|
||||
'fields' => array(
|
||||
'zone' => array(
|
||||
'description' => 'The 2-letter state abberviation.',
|
||||
'type' => 'varchar',
|
||||
'length' => 2,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'rate' => array(
|
||||
'description' => 'The tax rate.',
|
||||
'type' => 'float',
|
||||
'not null' => TRUE,
|
||||
'default' => 0.0,
|
||||
),
|
||||
'tax_shipping' => array(
|
||||
'description' => 'A flag indicating whether shipping costs are taxed.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('zone'),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Ubercart payment related tests
|
||||
*/
|
||||
|
||||
class UbercartPaymentPaneTestCase extends UbercartTestHelper {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Payment checkout pane',
|
||||
'description' => 'Ensures that the payment pane functions properly during checkout.',
|
||||
'group' => 'Ubercart',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DrupalWebTestCase::setUp().
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array('uc_payment', 'uc_payment_pack'));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPost('node/' . $this->product->nid, array(), t('Add to cart'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies checkout page presents all enabled payment methods.
|
||||
*/
|
||||
public function testPaymentMethodOptions() {
|
||||
// No payment methods.
|
||||
variable_set('uc_payment_method_check_checkout', FALSE);
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertText('Checkout cannot be completed without any payment methods enabled. Please contact an administrator to resolve the issue.');
|
||||
|
||||
// Single payment method.
|
||||
variable_set('uc_payment_method_check_checkout', TRUE);
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertNoText('Select a payment method from the following options.');
|
||||
$this->assertFieldByXPath("//input[@name='panes[payment][payment_method]' and @disabled='disabled']");
|
||||
|
||||
// Multiple payment methods.
|
||||
variable_set('uc_payment_method_other_checkout', TRUE);
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertText('Select a payment method from the following options.');
|
||||
$this->assertNoFieldByXPath("//input[@name='panes[payment][payment_method]' and @disabled='disabled']");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests operation of uc_payment_show_order_total_preview variable.
|
||||
*/
|
||||
public function testOrderTotalPreview() {
|
||||
variable_set('uc_payment_show_order_total_preview', TRUE);
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertText('Order total:');
|
||||
|
||||
variable_set('uc_payment_show_order_total_preview', FALSE);
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertNoText('Order total:');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests free orders.
|
||||
*/
|
||||
public function testFreeOrders() {
|
||||
$free_product = $this->createProduct(array('sell_price' => 0));
|
||||
variable_set('uc_payment_method_check_checkout', TRUE);
|
||||
|
||||
// Check that paid products cannot be purchased for free.
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertText('Check or money order');
|
||||
$this->assertNoText('No payment required');
|
||||
$this->assertNoText('Subtotal: $0.00');
|
||||
|
||||
// Check that a mixture of free and paid products cannot be purchased for free.
|
||||
$this->drupalPost('node/' . $free_product->nid, array(), t('Add to cart'));
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertText('Check or money order');
|
||||
$this->assertNoText('No payment required');
|
||||
$this->assertNoText('Subtotal: $0.00');
|
||||
|
||||
// Check that free products can be purchased successfully with no payment.
|
||||
$this->drupalPost('cart', array(), t('Remove'));
|
||||
$this->drupalPost('cart', array(), t('Remove'));
|
||||
$this->drupalPost('node/' . $free_product->nid, array(), t('Add to cart'));
|
||||
$this->drupalGet('cart/checkout');
|
||||
$this->assertNoText('Check or money order');
|
||||
$this->assertText('No payment required');
|
||||
$this->assertText('Subtotal: $0.00');
|
||||
|
||||
// Check that this is the only available payment method.
|
||||
$this->assertNoText('Select a payment method from the following options.');
|
||||
$this->assertFieldByXPath("//input[@name='panes[payment][payment_method]' and @disabled='disabled']");
|
||||
}
|
||||
}
|
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Payment administration menu items.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Displays an overview of the available payment methods.
|
||||
*
|
||||
* @see theme_uc_payment_methods_table()
|
||||
*/
|
||||
function uc_payment_methods_form($form, &$form_state) {
|
||||
$methods = _uc_payment_method_list();
|
||||
|
||||
$form['pmtable'] = array(
|
||||
'#theme' => 'uc_payment_method_table',
|
||||
);
|
||||
|
||||
foreach ($methods as $id => $method) {
|
||||
$form['pmtable'][$id]['uc_payment_method_' . $id . '_checkout'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => check_plain($method['name']),
|
||||
'#default_value' => variable_get('uc_payment_method_' . $id . '_checkout', $method['checkout']),
|
||||
);
|
||||
$form['pmtable'][$id]['uc_payment_method_' . $id . '_weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#default_value' => variable_get('uc_payment_method_' . $id . '_weight', $method['weight']),
|
||||
'#attributes' => array('class' => array('uc-payment-method-weight')),
|
||||
);
|
||||
|
||||
if (empty($method['no_gateway'])) {
|
||||
$gateways = _uc_payment_gateway_list($id, TRUE);
|
||||
$options = array();
|
||||
foreach ($gateways as $gateway_id => $gateway) {
|
||||
$options[$gateway_id] = $gateway['title'];
|
||||
}
|
||||
if ($options) {
|
||||
$form['pmtable'][$id]['uc_payment_method_' . $id . '_checkout']['#title'] .= ' (' . t('includes %gateways', array('%gateways' => implode(', ', $options))) . ')';
|
||||
}
|
||||
}
|
||||
|
||||
$links = array();
|
||||
$null = NULL;
|
||||
$method_settings = $method['callback']('settings', $null, array(), $form_state);
|
||||
if (is_array($method_settings)) {
|
||||
$links['settings'] = array(
|
||||
'title' => t('settings'),
|
||||
'href' => 'admin/store/settings/payment/method/' . $id,
|
||||
);
|
||||
}
|
||||
|
||||
$links['conditions'] = array(
|
||||
'title' => t('conditions'),
|
||||
'href' => 'admin/store/settings/payment/manage/uc_payment_method_' . $id,
|
||||
);
|
||||
|
||||
$form['pmtable'][$id]['settings'] = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $links,
|
||||
'#attributes' => array('class' => array('links', 'inline')),
|
||||
);
|
||||
}
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Themes the table that displays available payment methods.
|
||||
*
|
||||
* @see uc_payment_methods_form()
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_uc_payment_method_table($variables) {
|
||||
$form = $variables['form'];
|
||||
|
||||
drupal_add_tabledrag('uc-payment-methods', 'order', 'sibling', 'uc-payment-method-weight');
|
||||
|
||||
$header = array(t('Payment method'), t('List position'), t('Operations'));
|
||||
|
||||
$rows = array();
|
||||
foreach (element_children($form) as $method) {
|
||||
$row = array(
|
||||
drupal_render($form[$method]['uc_payment_method_' . $method . '_checkout']),
|
||||
drupal_render($form[$method]['uc_payment_method_' . $method . '_weight']),
|
||||
drupal_render($form[$method]['settings']),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
'data' => $row,
|
||||
'class' => array('draggable'),
|
||||
);
|
||||
}
|
||||
|
||||
return theme('table', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'attributes' => array('id' => 'uc-payment-methods'),
|
||||
'empty' => t('No payment methods are available. Modules providing payment methods must first be enabled on the !modules administration page under the "Ubercart - payment" fieldset.', array('!modules' => l(t('Modules'), 'admin/modules'))),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays settings for a single payment method.
|
||||
*/
|
||||
function uc_payment_method_settings_form($form, &$form_state, $method_id) {
|
||||
$callback = _uc_payment_method_data($method_id, 'callback');
|
||||
$null = NULL;
|
||||
$form = $callback('settings', $null, array(), $form_state);
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a list of payments attached to an order.
|
||||
*
|
||||
* @see uc_payment_by_order_form_validate()
|
||||
* @see uc_payment_by_order_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_payment_by_order_form($form, &$form_state, $order) {
|
||||
$form['#attached']['css'][] = drupal_get_path('module', 'uc_payment') . '/uc_payment.css';
|
||||
|
||||
$total = $order->order_total;
|
||||
$payments = uc_payment_load_payments($order->order_id);
|
||||
|
||||
$form['order_total'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Order total'),
|
||||
'#theme' => 'uc_price',
|
||||
'#price' => $total,
|
||||
);
|
||||
$form['payments'] = tapir_get_table('uc_payments_table');
|
||||
$form['payments']['#weight'] = 10;
|
||||
|
||||
if ($payments !== FALSE) {
|
||||
foreach ($payments as $payment) {
|
||||
$form['payments'][$payment->receipt_id]['received'] = array(
|
||||
'#markup' => format_date($payment->received, 'custom', variable_get('date_format_uc_store', 'm/d/Y') . '<b\r />H:i:s'),
|
||||
);
|
||||
$form['payments'][$payment->receipt_id]['user'] = array(
|
||||
'#markup' => theme('uc_uid', array('uid' => $payment->uid)),
|
||||
);
|
||||
$form['payments'][$payment->receipt_id]['method'] = array(
|
||||
'#markup' => ($payment->method == '') ? t('Unknown') : $payment->method,
|
||||
);
|
||||
$form['payments'][$payment->receipt_id]['amount'] = array(
|
||||
'#theme' => 'uc_price',
|
||||
'#price' => $payment->amount,
|
||||
);
|
||||
$total -= $payment->amount;
|
||||
$form['payments'][$payment->receipt_id]['balance'] = array(
|
||||
'#theme' => 'uc_price',
|
||||
'#price' => $total,
|
||||
);
|
||||
$form['payments'][$payment->receipt_id]['comment'] = array(
|
||||
'#markup' => ($payment->comment == '') ? '-' : filter_xss_admin($payment->comment),
|
||||
);
|
||||
if (user_access('delete payments')) {
|
||||
$action_value = l(t('Delete'), 'admin/store/orders/' . $order->order_id . '/payments/'
|
||||
. $payment->receipt_id . '/delete');
|
||||
}
|
||||
else {
|
||||
$action_value = '-';
|
||||
}
|
||||
$form['payments'][$payment->receipt_id]['action'] = array(
|
||||
'#markup' => $action_value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$form['balance'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Current balance'),
|
||||
'#theme' => 'uc_price',
|
||||
'#price' => $total,
|
||||
);
|
||||
$form['order_id'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $order->order_id,
|
||||
);
|
||||
|
||||
if (user_access('manual payments')) {
|
||||
$form['payments']['new']['received'] = array(
|
||||
'#type' => 'date',
|
||||
'#default_value' => array(
|
||||
'month' => format_date(REQUEST_TIME, 'custom', 'n'),
|
||||
'day' => format_date(REQUEST_TIME, 'custom', 'j'),
|
||||
'year' => format_date(REQUEST_TIME, 'custom', 'Y'),
|
||||
),
|
||||
);
|
||||
$form['payments']['new']['user'] = array(
|
||||
'#markup' => '-',
|
||||
);
|
||||
$methods = _uc_payment_method_list();
|
||||
foreach ($methods as $id => $method) {
|
||||
$options[$id] = $method['name'];
|
||||
}
|
||||
$form['payments']['new']['method'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Method'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $options,
|
||||
);
|
||||
$form['payments']['new']['amount'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Amount'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 6,
|
||||
);
|
||||
$form['payments']['new']['balance'] = array(
|
||||
'#markup' => '-',
|
||||
);
|
||||
$form['payments']['new']['comment'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Comment'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 32,
|
||||
'#maxlength' => 256,
|
||||
);
|
||||
$form['payments']['new']['action'] = array('#type' => 'actions');
|
||||
$form['payments']['new']['action']['action'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Enter'),
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validation for uc_payment_by_order_form().
|
||||
*
|
||||
* @see uc_payment_by_order_form()
|
||||
* @see uc_payment_by_order_form_submit()
|
||||
*/
|
||||
function uc_payment_by_order_form_validate($form, &$form_state) {
|
||||
if (!is_numeric($form_state['values']['payments']['new']['amount'])) {
|
||||
form_set_error('payments][new][amount', t('You must enter a number for the amount.'));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_payment_by_order_form().
|
||||
*
|
||||
* @see uc_payment_by_order_form()
|
||||
* @see uc_payment_by_order_form_validate()
|
||||
*/
|
||||
function uc_payment_by_order_form_submit($form, &$form_state) {
|
||||
global $user;
|
||||
|
||||
$payment = $form_state['values']['payments']['new'];
|
||||
$received = strtotime($payment['received']['year'] . '-' . $payment['received']['month'] . '-' . $payment['received']['day'] . ' 00:00:00');
|
||||
|
||||
// If the value entered is today, use the exact timestamp instead
|
||||
$startofday = mktime(0, 0, 0);
|
||||
|
||||
if ($received == $startofday) {
|
||||
$received = REQUEST_TIME;
|
||||
}
|
||||
|
||||
uc_payment_enter($form_state['values']['order_id'], $payment['method'], $payment['amount'], $user->uid, '', $payment['comment'], $received);
|
||||
|
||||
drupal_set_message(t('Payment entered.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation form to delete a payment from an order.
|
||||
*
|
||||
* @see uc_payment_delete_confirm_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_payment_delete_confirm_form($form, &$form_state, $order, $payment) {
|
||||
// Make sure the payment is for the specified order.
|
||||
if ($payment->order_id != $order->order_id) {
|
||||
drupal_set_message(t('An error loading the payment information occurred.'));
|
||||
drupal_goto('admin/store/orders/' . $order->order_id . '/payments');
|
||||
}
|
||||
|
||||
$desc = '<strong>' . t('Payment information:') . '</strong> '
|
||||
. t('@method payment of @amount received on @date.', array('@method' => $payment->method, '@amount' => uc_currency_format($payment->amount), '@date' => format_date($payment->received, 'short')));
|
||||
|
||||
$form['order_id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $order->order_id
|
||||
);
|
||||
$form['receipt_id'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $payment->receipt_id,
|
||||
);
|
||||
|
||||
return confirm_form($form, t('Are you sure you want to delete this payment?'), 'admin/store/orders/' . $order->order_id . '/payments', $desc, t('Delete'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_payment_delete_confirm_form().
|
||||
*
|
||||
* @see uc_payment_delete_confirm_form()
|
||||
*/
|
||||
function uc_payment_delete_confirm_form_submit($form, &$form_state) {
|
||||
uc_payment_delete($form_state['values']['receipt_id']);
|
||||
|
||||
drupal_set_message(t('Payment deleted.'));
|
||||
|
||||
$form_state['redirect'] = 'admin/store/orders/' . $form_state['values']['order_id'] . '/payments';
|
||||
}
|
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Payment module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Takes action when a payment is entered for an order.
|
||||
*
|
||||
* @param $order
|
||||
* The order object.
|
||||
* @param $method
|
||||
* The name of the payment method used.
|
||||
* @param $amount
|
||||
* The value of the payment.
|
||||
* @param $account
|
||||
* The user account that entered the order. When the payment is entered
|
||||
* during checkout, this is probably the order's user. Otherwise, it is
|
||||
* likely a store administrator.
|
||||
* @param $data
|
||||
* Extra data associated with the transaction.
|
||||
* @param $comment
|
||||
* Any comments from the user about the transaction.
|
||||
*/
|
||||
function hook_uc_payment_entered($order, $method, $amount, $account, $data, $comment) {
|
||||
drupal_set_message(t('User @uid entered a @method payment of @amount for order @order_id.',
|
||||
array(
|
||||
'@uid' => $account->uid,
|
||||
'@method' => $method,
|
||||
'@amount' => uc_currency_format($amount),
|
||||
'@order_id' => $order->order_id,
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers credit card payment gateway callbacks.
|
||||
*
|
||||
* Payment gateways handle credit card payments directly, without needing to
|
||||
* redirect off-site.
|
||||
*
|
||||
* @see http://www.ubercart.org/docs/api/hook_uc_payment_gateway
|
||||
* @see hook_uc_payment_gateway_charge()
|
||||
*
|
||||
* @return
|
||||
* Returns an array of payment gateways, keyed by the gateway ID, and with
|
||||
* the following members:
|
||||
* - "title": the human-readable name of the payment method.
|
||||
* - "description": a human-readable description of the payment method.
|
||||
* - "settings": A callback function that returns the gateway settings form.
|
||||
* - "credit": A callback function that processes the credit card. See
|
||||
* hook_uc_payment_gateway_charge() for details.
|
||||
*/
|
||||
function hook_uc_payment_gateway() {
|
||||
$gateways['test_gateway'] = array(
|
||||
'title' => t('Test gateway'),
|
||||
'description' => t('Process credit card payments through the Test Gateway.'),
|
||||
'credit' => 'test_gateway_charge',
|
||||
);
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit card charge callback.
|
||||
*
|
||||
* Called when a credit card should be processed. Credit card details supplied
|
||||
* by the user are available in $order->payment_details[].
|
||||
*
|
||||
* @see hook_uc_payment_gateway()
|
||||
* @see uc_authorizenet_charge()
|
||||
* @see test_gateway_charge()
|
||||
*
|
||||
* @param $order_id
|
||||
* The order ID that the payment relates to.
|
||||
* @param $amount
|
||||
* The amount that should be charged.
|
||||
* @param $data
|
||||
* An array of data related to the charge. By default, includes a 'txn_type'
|
||||
* key which defines the transaction type, usually UC_CREDIT_AUTH_ONLY
|
||||
* or UC_CREDIT_AUTH_CAPTURE.
|
||||
*
|
||||
* @return
|
||||
* Returns an associative array with the following members:
|
||||
* - "success": TRUE if the transaction succeeded, FALSE otherwise.
|
||||
* - "message": a human-readable message describing the result of the
|
||||
* transaction.
|
||||
* - "log_payment": TRUE if the transaction should be regarded as a
|
||||
* successful payment.
|
||||
*/
|
||||
function hook_uc_payment_gateway_charge($order_id, $amount, $data) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters payment gateways.
|
||||
*
|
||||
* @param $gateways
|
||||
* Array of payment gateways passed by reference. Array is structured like
|
||||
* the return value of hook_uc_payment_gateway().
|
||||
*/
|
||||
function hook_uc_payment_gateway_alter(&$gateways) {
|
||||
// Change the title of the test gateway.
|
||||
$gateways['test_gateway']['title'] = t('Altered test gateway title.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callbacks for payment methods.
|
||||
*
|
||||
* Payment methods are different ways to collect payment. By default, Ubercart
|
||||
* comes with support for check, credit card, and generic payments. Payment
|
||||
* methods show up at checkout or on the order administration screens, and they
|
||||
* collect different sorts of information from the user that is used to process
|
||||
* or track the payment.
|
||||
*
|
||||
* @see hook_uc_payment_method_callback()
|
||||
*
|
||||
* @return
|
||||
* An array of payment methods. The array contains a sub-array for each
|
||||
* payment method, with the machine-readable type name as the key. Required
|
||||
* attributes:
|
||||
* - "name": the human-readable name of the payment method.
|
||||
* - "title": the human-readable title of the payment method, displayed
|
||||
* during checkout.
|
||||
* - "desc": a human-readable description of the payment method.
|
||||
* - "callback": a callback function that handles operations that the method
|
||||
* may need to perform. See hook_uc_payment_method_callback()
|
||||
* - "weight": the default weight of the payment method.
|
||||
* - "checkout": if TRUE, the payment method will be enabled by default.
|
||||
* - "no_gateway": should be set to TRUE, except for uc_credit which uses
|
||||
* payment gateways.
|
||||
* - "redirect": if set, this payment method redirects off site; this key
|
||||
* specifies a callback function which will be used to generate the form
|
||||
* that redirects the user to the payment gateway pages.
|
||||
*/
|
||||
function hook_uc_payment_method() {
|
||||
$methods['check'] = array(
|
||||
'name' => t('Check'),
|
||||
'title' => t('Check or money order'),
|
||||
'desc' => t('Pay by mailing a check or money order.'),
|
||||
'callback' => 'uc_payment_method_callback',
|
||||
'weight' => 1,
|
||||
'checkout' => TRUE,
|
||||
);
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function to perform various operations for a payment method.
|
||||
*
|
||||
* Possible operations are as follows:
|
||||
* - "cart-details": The payment method has been selected at checkout. Return
|
||||
* a form or render array to be displayed in the payment method pane.
|
||||
* - "cart-process": Called when the user submits the checkout form with this
|
||||
* payment method selected, used to process any form elements output by the
|
||||
* 'cart-details' op. Return FALSE to abort the checkout process, or NULL or
|
||||
* TRUE to continue with checkout.
|
||||
* - "cart-review": Called when the checkout review page is being displayed.
|
||||
* Return an array of data to be displayed below the payment method title on
|
||||
* the checkout review page.
|
||||
* - "customer-view": Called when the order is being displayed to a customer.
|
||||
* Return a render array to be displayed to customers.
|
||||
* - "order-delete": Called when an order is being deleted. Payment methods
|
||||
* should clean up any extra data they stored related to the order.
|
||||
* - "order-details": Called when an order is being edited by an administrator.
|
||||
* Return a string or a form array to be displayed to the administator.
|
||||
* - "order-load": Called from hook_uc_order('load') when this payment method
|
||||
* is selected for the order.
|
||||
* - "order-process": Called when an order has been edited by an administrator.
|
||||
* Process any form elements returned by the "order-details" op.
|
||||
* - "order-save": Called from hook_uc_order('save') when this payment method
|
||||
* is selected for the order.
|
||||
* - "order-submit": Called from hook_uc_order('submit') when this payment
|
||||
* method is selected for the order.
|
||||
* - "order-view": Called when the order is being displayed on the order admin
|
||||
* pages. Return a render array to be displayed to administrators.
|
||||
* - "settings": Called when the payment methods page is being displayed.
|
||||
* Return a system settings form array to configure the payment method.
|
||||
*
|
||||
* @see hook_uc_payment_method()
|
||||
*
|
||||
* @param $op
|
||||
* The operation being performed.
|
||||
* @param &$order
|
||||
* The order object that relates to this operation.
|
||||
* @param $form
|
||||
* Where applicable, the form object that relates to this operation.
|
||||
* @param &$form_state
|
||||
* Where applicable, the form state that relates to this operation.
|
||||
*
|
||||
* @return
|
||||
* Dependent on $op.
|
||||
*/
|
||||
function hook_uc_payment_method_callback($op, &$order, $form = NULL, &$form_state = NULL) {
|
||||
switch ($op) {
|
||||
case 'cart-details':
|
||||
return array('#markup' => t('Continue with checkout to complete payment.'));
|
||||
|
||||
case 'settings':
|
||||
$form['uc_payment_method_account_number'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Payment gateway account number'),
|
||||
'#default_value' => variable_get('uc_payment_method_account_number', ''),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter payment methods.
|
||||
*
|
||||
* @param $methods
|
||||
* Array of payment methods passed by reference. Array is structured like
|
||||
* the return value of hook_uc_payment_method().
|
||||
*/
|
||||
function hook_uc_payment_method_alter(&$methods) {
|
||||
// Change the title of the Check payment method.
|
||||
$methods['check']['title'] = t('Cheque');
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter payment methods available at checkout.
|
||||
*
|
||||
* @param $methods
|
||||
* Array of payment methods passed by reference. Keys are payment method IDs,
|
||||
* strings are payment method titles.
|
||||
* @param $order
|
||||
* The order that is being checked out.
|
||||
*/
|
||||
function hook_uc_payment_method_checkout_alter(&$methods, $order) {
|
||||
// Remove the Check payment method for orders under $100.
|
||||
if ($order->order_total < 100) {
|
||||
unset($methods['check']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @file
|
||||
* Styles for uc_payment module.
|
||||
*/
|
||||
|
||||
.uc-credit-cctype {
|
||||
vertical-align: middle;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
#paypal-includes {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#payment-details {
|
||||
border-top: solid 1px #bbb;
|
||||
clear: both;
|
||||
margin-top: 1em;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.payment-details-cod .form-item {
|
||||
display: block;
|
||||
float: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.payment-details-credit .form-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.payment-details-credit label {
|
||||
clear: left;
|
||||
float: left;
|
||||
margin: 2px 0;
|
||||
padding-top: 5px;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
.payment-details-credit input,
|
||||
.payment-details-credit select {
|
||||
float: left;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.form-item-panes-payment-details-cc-start-year label,
|
||||
.form-item-panes-payment-details-cc-exp-year label,
|
||||
.form-item-cc-data-cc-start-year label,
|
||||
.form-item-cc-data-cc-exp-year label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.payment-details-credit .field-suffix {
|
||||
float: left;
|
||||
margin: 2px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
img.uc-2checkout-logo {
|
||||
position: relative;
|
||||
left: 2.2em;
|
||||
}
|
||||
|
||||
#line-items-div {
|
||||
border: 1px solid #bbb;
|
||||
float: right;
|
||||
margin: 0 0 1em 1em;
|
||||
}
|
||||
|
||||
#line-items-div table {
|
||||
margin: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#line-items-div td {
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
|
||||
#line-items-div td.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#uc-payment-by-order-form .form-type-item {
|
||||
float: left;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
#uc-payment-by-order-form table {
|
||||
clear: left;
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
name = Payment
|
||||
description = Enables payments to be taken at checkout.
|
||||
dependencies[] = uc_order
|
||||
dependencies[] = uc_store
|
||||
package = Ubercart - core (optional)
|
||||
core = 7.x
|
||||
|
||||
; Test cases
|
||||
files[] = tests/uc_payment.test
|
||||
|
||||
configure = admin/store/settings/payment
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity Metadata hooks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info().
|
||||
*/
|
||||
function uc_payment_entity_property_info() {
|
||||
return array(
|
||||
'uc_order' => array(
|
||||
'properties' => array(
|
||||
'payment_balance' => array(
|
||||
'type' => 'decimal',
|
||||
'label' => t('Balance'),
|
||||
'description' => t('The total amount remaining for the order.'),
|
||||
'getter callback' => 'uc_payment_balance',
|
||||
'query callback' => 'entity_metadata_table_query',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter().
|
||||
*/
|
||||
function uc_payment_entity_property_info_alter(&$info) {
|
||||
$info['uc_order']['properties']['payment_method']['options list'] = 'uc_payment_method_options_list';
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_payment module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_payment_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_payment_receipts'] = array(
|
||||
'description' => 'Stores completed payments.',
|
||||
'fields' => array(
|
||||
'receipt_id' => array(
|
||||
'description' => 'Primary key: the payment receipt ID.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'method' => array(
|
||||
'description' => 'The payment method.',
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'amount' => array(
|
||||
'description' => 'The payment amount in the store default currency.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'uid' => array(
|
||||
'description' => 'The {users}.uid who collected the payment.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'A serialized array of extra payment data.',
|
||||
'type' => 'text',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'comment' => array(
|
||||
'description' => 'A comment made on the payment.',
|
||||
'type' => 'text',
|
||||
),
|
||||
'received' => array(
|
||||
'description' => 'The Unix timestamp indicating when the payment was received.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'order_id' => array('order_id'),
|
||||
),
|
||||
'primary key' => array('receipt_id'),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function uc_payment_install() {
|
||||
$t = get_t();
|
||||
|
||||
db_insert('uc_order_statuses')
|
||||
->fields(array(
|
||||
'order_status_id' => 'payment_received',
|
||||
'title' => $t('Payment received'),
|
||||
'state' => 'payment_received',
|
||||
'weight' => 10,
|
||||
'locked' => 1,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_payment_uninstall() {
|
||||
db_delete('variable')
|
||||
->condition(db_or()
|
||||
->condition('name', 'uc_pg_%', 'LIKE')
|
||||
->condition('name', 'uc_payment_%', 'LIKE'))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_payment_update_last_removed() {
|
||||
// 7.x-3.0-beta2 and earlier were installed with schema version 0,
|
||||
// which causes update.php to fail.
|
||||
return drupal_get_installed_schema_version('uc_payment') == 0 ? 0 : 6002;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unused variables.
|
||||
*/
|
||||
function uc_payment_update_7000() {
|
||||
variable_del('uc_payment_tracking');
|
||||
variable_del('uc_payment_deleting');
|
||||
variable_del('uc_payment_logging');
|
||||
variable_del('uc_default_payment_msg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase maximum length of comment field.
|
||||
*/
|
||||
function uc_payment_update_7001() {
|
||||
db_change_field('uc_payment_receipts', 'comment', 'comment', array(
|
||||
'description' => 'A comment made on the payment.',
|
||||
'type' => 'text',
|
||||
));
|
||||
}
|
@@ -0,0 +1,700 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Defines the payment API that lets payment modules interact with Ubercart.
|
||||
*
|
||||
* The payment system in Ubercart relies on hooks to let the main program know
|
||||
* what payment modules are installed and what their current settings are. The
|
||||
* customer can choose a payment type at checkout, and the proper information
|
||||
* will be collected to complete the purchase.
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/uc_payment_checkout_pane.inc';
|
||||
require_once dirname(__FILE__) . '/uc_payment_order_pane.inc';
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function uc_payment_menu() {
|
||||
$items['admin/store/settings/payment'] = array(
|
||||
'title' => 'Payment methods',
|
||||
'description' => 'Choose and configure available payment methods.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_payment_methods_form'),
|
||||
'access arguments' => array('administer store'),
|
||||
'file' => 'uc_payment.admin.inc',
|
||||
);
|
||||
$items['admin/store/settings/payment/method/%'] = array(
|
||||
'title callback' => 'uc_payment_method_title',
|
||||
'title arguments' => array(5),
|
||||
'description' => 'Configures a specific payment method.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_payment_method_settings_form', 5),
|
||||
'access arguments' => array('administer store'),
|
||||
'file' => 'uc_payment.admin.inc',
|
||||
);
|
||||
$items += rules_ui()->config_menu('admin/store/settings/payment');
|
||||
|
||||
$items['admin/store/orders/%uc_order/payments'] = array(
|
||||
'title' => 'Payments',
|
||||
'description' => 'Show payments made against an order.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_payment_by_order_form', 3),
|
||||
'access arguments' => array('view payments'),
|
||||
'weight' => 5,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'uc_payment.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/payments/%uc_payment/delete'] = array(
|
||||
'title' => 'Delete payment?',
|
||||
'description' => 'Delete payment?',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_payment_delete_confirm_form', 3, 5),
|
||||
'access arguments' => array('delete payments'),
|
||||
'type' => MENU_CALLBACK,
|
||||
'file' => 'uc_payment.admin.inc',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for payment method settings.
|
||||
*/
|
||||
function uc_payment_method_title($method_id) {
|
||||
return t('!method settings', array('!method' => _uc_payment_method_data($method_id, 'name')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function uc_payment_permission() {
|
||||
return array(
|
||||
'view payments' => array(
|
||||
'title' => t('View payments'),
|
||||
),
|
||||
'manual payments' => array(
|
||||
'title' => t('Manual payments'),
|
||||
'description' => t('Enter payments manually.'),
|
||||
),
|
||||
'delete payments' => array(
|
||||
'title' => t('Delete payments'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function uc_payment_theme() {
|
||||
return array(
|
||||
'uc_payment_totals' => array(
|
||||
'variables' => array('order' => NULL),
|
||||
'file' => 'uc_payment.theme.inc',
|
||||
),
|
||||
'uc_payment_method_table' => array(
|
||||
'render element' => 'form',
|
||||
'file' => 'uc_payment.admin.inc',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for uc_cart_view_form().
|
||||
*
|
||||
* Adds express buttons for enabled payment modules directly to the cart page.
|
||||
*/
|
||||
function uc_payment_form_uc_cart_view_form_alter(&$form, &$form_state) {
|
||||
$methods = _uc_payment_method_list();
|
||||
foreach ($methods as $id => $method) {
|
||||
if ($method['checkout'] && isset($method['express']) && $express = $method['express'](array(), $form_state)) {
|
||||
$form['actions']['checkout'][$id] = $express;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for uc_cart_checkout_review_form().
|
||||
*
|
||||
* If a payment method redirects off-site, add the required form to the review
|
||||
* page.
|
||||
*/
|
||||
function uc_payment_form_uc_cart_checkout_review_form_alter(&$form, &$form_state) {
|
||||
$order = $form_state['uc_order'];
|
||||
|
||||
if ($redirect = _uc_payment_method_data($order->payment_method, 'redirect')) {
|
||||
unset($form['actions']['submit']);
|
||||
$suffix = drupal_get_form($redirect, $order);
|
||||
$form['#suffix'] = drupal_render($suffix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_order().
|
||||
*/
|
||||
function uc_payment_uc_order($op, $order) {
|
||||
if (!isset($order->payment_method)) {
|
||||
$order->payment_method = '';
|
||||
}
|
||||
|
||||
switch ($op) {
|
||||
case 'submit':
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func)) {
|
||||
return $func('order-submit', $order);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'load':
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func)) {
|
||||
$func('order-load', $order);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'save':
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func)) {
|
||||
$func('order-save', $order);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'can_delete':
|
||||
if (uc_payment_load_payments($order->order_id) !== FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
db_delete('uc_payment_receipts')
|
||||
->condition('order_id', $order->order_id)
|
||||
->execute();
|
||||
|
||||
// Call each payment method to delete method-specific data from the
|
||||
// database.
|
||||
$methods = _uc_payment_method_list();
|
||||
foreach ($methods as $method) {
|
||||
$func = $method['callback'];
|
||||
if (function_exists($func)) {
|
||||
$func('order-delete', $order);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_checkout_pane().
|
||||
*/
|
||||
function uc_payment_uc_checkout_pane() {
|
||||
$panes['payment'] = array(
|
||||
'title' => t('Payment method'),
|
||||
'desc' => t('Select a payment method from the enabled payment modules.'),
|
||||
'callback' => 'uc_checkout_pane_payment',
|
||||
'weight' => 6,
|
||||
);
|
||||
|
||||
return $panes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_order_pane().
|
||||
*/
|
||||
function uc_payment_uc_order_pane() {
|
||||
$panes['payment'] = array(
|
||||
'callback' => 'uc_order_pane_payment',
|
||||
'title' => t('Payment'),
|
||||
'desc' => t('Specify and collect payment for an order.'),
|
||||
'class' => 'pos-left',
|
||||
'weight' => 4,
|
||||
'show' => array('view', 'edit', 'customer'),
|
||||
);
|
||||
|
||||
return $panes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_order_state().
|
||||
*/
|
||||
function uc_payment_uc_order_state() {
|
||||
$states['payment_received'] = array(
|
||||
'title' => t('Payment received'),
|
||||
'weight' => 10,
|
||||
'scope' => 'general',
|
||||
);
|
||||
|
||||
return $states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_payment_method().
|
||||
*/
|
||||
function uc_payment_uc_payment_method() {
|
||||
$methods['free_order'] = array(
|
||||
'name' => t('Free order'),
|
||||
'title' => t('No payment required'),
|
||||
'desc' => t('Allow customers with !zero order totals to checkout without paying.', array('!zero' => uc_currency_format(0))),
|
||||
'callback' => 'uc_payment_method_free_order',
|
||||
'checkout' => TRUE,
|
||||
'no_gateway' => TRUE,
|
||||
'weight' => 0,
|
||||
);
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_payment_method_checkout_alter().
|
||||
*/
|
||||
function uc_payment_uc_payment_method_checkout_alter(&$methods, $order) {
|
||||
if (isset($methods['free_order'])) {
|
||||
if ($order->order_total < 0.01) {
|
||||
// Unset all other payment methods if this is a free order.
|
||||
foreach (array_keys($methods) as $key) {
|
||||
if ($key != 'free_order') {
|
||||
unset($methods[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Disallow this payment method if the order is not free.
|
||||
unset($methods['free_order']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment method callback for free orders.
|
||||
*
|
||||
* @see uc_payment_uc_payment_method()
|
||||
*/
|
||||
function uc_payment_method_free_order($op, &$order) {
|
||||
switch ($op) {
|
||||
case 'cart-details':
|
||||
return array(
|
||||
'#markup' => t('Continue with checkout to complete your order.'),
|
||||
);
|
||||
|
||||
case 'order-submit':
|
||||
if ($order->order_total >= 0.01) {
|
||||
return array(array(
|
||||
'pass' => FALSE,
|
||||
'message' => t('We cannot process your order without payment.'),
|
||||
));
|
||||
}
|
||||
|
||||
uc_payment_enter($order->order_id, 'free_order', 0, 0, NULL, t('Checkout completed for a free order.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted list of line items for an order total preview.
|
||||
*
|
||||
* @param $return
|
||||
* TRUE or FALSE to specify whether or not to return the results instead of
|
||||
* printing them and exiting.
|
||||
* @param $order
|
||||
* Optionally pass in a full order object to use instead of finding it in the
|
||||
* $_POST data.
|
||||
*
|
||||
* @return
|
||||
* The formatted HTML of the order total preview if $return is set to TRUE.
|
||||
*/
|
||||
function uc_payment_get_totals($form, $form_state) {
|
||||
$commands[] = ajax_command_replace('#line-items-div', trim(drupal_render($form['panes']['payment']['line_items'])));
|
||||
|
||||
return array('#type' => 'ajax', '#commands' => $commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* TAPIr table definition for uc_payments_table.
|
||||
*/
|
||||
function uc_payments_table() {
|
||||
$table = array(
|
||||
'#type' => 'tapir_table',
|
||||
'#tree' => TRUE,
|
||||
'#columns' => array(
|
||||
'received' => array(
|
||||
'cell' => t('Received'),
|
||||
'weight' => 0,
|
||||
),
|
||||
'user' => array(
|
||||
'cell' => t('User'),
|
||||
'weight' => 1,
|
||||
),
|
||||
'method' => array(
|
||||
'cell' => t('Method'),
|
||||
'weight' => 2,
|
||||
),
|
||||
'amount' => array(
|
||||
'cell' => t('Amount'),
|
||||
'weight' => 3,
|
||||
),
|
||||
'balance' => array(
|
||||
'cell' => t('Balance'),
|
||||
'weight' => 4,
|
||||
),
|
||||
'comment' => array(
|
||||
'cell' => t('Comment'),
|
||||
'weight' => 5,
|
||||
),
|
||||
'action' => array(
|
||||
'cell' => t('Action'),
|
||||
'weight' => 6,
|
||||
),
|
||||
),
|
||||
'#rows' => array(),
|
||||
);
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a payment through an enabled payment gateway.
|
||||
*
|
||||
* @param $method
|
||||
* The ID of the payment method to use to process the payment.
|
||||
* @param $order_id
|
||||
* The ID of the order associated with this payment.
|
||||
* @param $amount
|
||||
* The amount of the payment we're attempting to collect.
|
||||
* @param $data
|
||||
* An array of data passed on to the payment gateway module used to process
|
||||
* the payment for the specified payment method.
|
||||
* @param $default
|
||||
* TRUE or FALSE to indicate we're forcing the use of the default gateway for
|
||||
* the specified payment method. When TRUE, admin messages related to the
|
||||
* payment will be hidden from display so customers don't see them.
|
||||
* @param $selected
|
||||
* The ID of a payment gateway to use to process the payment; normally comes
|
||||
* from the payment gateway select form.
|
||||
* @param $redirect
|
||||
* TRUE or FALSE to indicate whether or not to redirect back to the admin
|
||||
* order view page for the order referenced in $order_id.
|
||||
*
|
||||
* @return
|
||||
* TRUE or FALSE indicating whether or not the payment was processed.
|
||||
*/
|
||||
function uc_payment_process_payment($method, $order_id, $amount, $data = NULL, $default = FALSE, $selected = NULL, $redirect = TRUE) {
|
||||
$result = array();
|
||||
|
||||
// Get an array of enabled payment gateways available for the payment method.
|
||||
$gateways = _uc_payment_gateway_list($method, TRUE);
|
||||
|
||||
// Fail if no gateways were found for the specified method.
|
||||
if (empty($gateways)) {
|
||||
// Display an error message if messages weren't silenced.
|
||||
if (!$default) {
|
||||
drupal_set_message(t('You are not able to process %type payments.', array('%type' => _uc_payment_method_data($method, 'name'))));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Find the default gateway if requested.
|
||||
if ($default) {
|
||||
$default = variable_get('uc_payment_' . $method . '_gateway', '');
|
||||
}
|
||||
|
||||
// If we only found one gateway for this payment method...
|
||||
if (count($gateways) == 1) {
|
||||
$gateway = reset($gateways);
|
||||
}
|
||||
elseif ($default && isset($gateways[$default])) {
|
||||
// The default gateway was forced.
|
||||
$gateway = $gateways[$default];
|
||||
}
|
||||
elseif ($selected && isset($gateways[$selected])) {
|
||||
// A specific gateway was selected.
|
||||
$gateway = $gateways[$selected];
|
||||
}
|
||||
else {
|
||||
// No gateway available.
|
||||
$gateway = array($method => '');
|
||||
}
|
||||
|
||||
// Check to see if the function exists and process the payment.
|
||||
if (function_exists($gateway[$method])) {
|
||||
// Reset the entity cache, so the latest data saved in the credit card cache
|
||||
// is guaranteed to be available in the charge function.
|
||||
uc_order_load($order_id, TRUE);
|
||||
|
||||
$result = $gateway[$method]($order_id, $amount, $data);
|
||||
}
|
||||
else {
|
||||
// Otherwise display an error message to administrators.
|
||||
$result['success'] = FALSE;
|
||||
$result['message'] = t('An error has occurred with your payment gateway. The charge function could not be found.');
|
||||
if (user_access('administer store')) {
|
||||
drupal_set_message($result['message']);
|
||||
}
|
||||
}
|
||||
|
||||
// If the payment processed successfully...
|
||||
if ($result['success'] === TRUE) {
|
||||
// Log the payment to the order if not disabled.
|
||||
if (!isset($result['log_payment']) || $result['log_payment'] !== FALSE) {
|
||||
uc_payment_enter($order_id, $method, $amount, empty($result['uid']) ? 0 : $result['uid'], empty($result['data']) ? '' : $result['data'], empty($result['comment']) ? '' : $result['comment']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Otherwise display the failure message in the logs.
|
||||
watchdog('uc_payment', 'Payment failed for order @order_id: @message', array('@order_id' => $order_id, '@message' => $result['message']), WATCHDOG_WARNING, l(t('view order'), 'admin/store/orders/' . $order_id));
|
||||
}
|
||||
|
||||
// If we have a message for display and aren't simply charging with the
|
||||
// default gateway for a customer...
|
||||
if (!empty($result['message']) && !$default) {
|
||||
drupal_set_message($result['message']);
|
||||
}
|
||||
|
||||
// Head back to the order if a redirect was specified.
|
||||
if ($redirect) {
|
||||
drupal_goto('admin/store/orders/' . $order_id);
|
||||
}
|
||||
|
||||
return $result['success'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enters a payment for an order.
|
||||
*
|
||||
* @param $order_id
|
||||
* The order ID to apply the payment to.
|
||||
* @param $method
|
||||
* The payment method ID.
|
||||
* @param $amount
|
||||
* The amount of the payment.
|
||||
* @param $uid
|
||||
* (optional) The user ID of the person logging the payment, or 0 if the
|
||||
* payment was processed automatically.
|
||||
* @param $data
|
||||
* (optional) Any data that should be serialized and stored with the
|
||||
* payment.
|
||||
* @param $comment
|
||||
* (optional) The comment to enter in the payment log.
|
||||
* @param $received
|
||||
* (optional) The timestamp at which the payment was received.
|
||||
*
|
||||
* @return
|
||||
* A unique ID identifying the payment.
|
||||
*/
|
||||
function uc_payment_enter($order_id, $method, $amount, $uid = 0, $data = NULL, $comment = '', $received = REQUEST_TIME) {
|
||||
$method_name = _uc_payment_method_data($method, 'review');
|
||||
if (empty($method_name)) {
|
||||
$method_name = _uc_payment_method_data($method, 'name');
|
||||
}
|
||||
if (is_null($method_name)) {
|
||||
$method_name = t('Other');
|
||||
}
|
||||
if (is_array($data)) {
|
||||
$data = serialize($data);
|
||||
}
|
||||
|
||||
$log_message = t('@method payment for @amount entered.', array('@method' => $method_name, '@amount' => uc_currency_format($amount)));
|
||||
uc_order_log_changes($order_id, array($log_message));
|
||||
|
||||
$receipt_id = db_insert('uc_payment_receipts')
|
||||
->fields(array(
|
||||
'order_id' => $order_id,
|
||||
'method' => $method_name,
|
||||
'amount' => $amount,
|
||||
'uid' => $uid,
|
||||
'data' => $data,
|
||||
'comment' => $comment,
|
||||
'received' => $received,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$order = uc_order_load($order_id, TRUE);
|
||||
$account = user_load($uid);
|
||||
|
||||
// Ensure user has an account before payment is made.
|
||||
if (module_exists('uc_cart')) {
|
||||
uc_cart_complete_sale($order);
|
||||
}
|
||||
|
||||
module_invoke_all('uc_payment_entered', $order, $method, $amount, $account, $data, $comment);
|
||||
rules_invoke_event('uc_payment_entered', $order, $account);
|
||||
|
||||
return $receipt_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a payment from the database.
|
||||
*/
|
||||
function uc_payment_delete($receipt_id) {
|
||||
if (!is_numeric($receipt_id)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$payment = uc_payment_load($receipt_id);
|
||||
$log_message = t('@method payment for @amount deleted.', array('@method' => $payment->method, '@amount' => uc_currency_format($payment->amount)));
|
||||
uc_order_log_changes($payment->order_id, array($log_message));
|
||||
|
||||
db_delete('uc_payment_receipts')
|
||||
->condition('receipt_id', $receipt_id)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the balance of payments on an order.
|
||||
*/
|
||||
function uc_payment_balance($order) {
|
||||
$total = $order->order_total;
|
||||
$payments = uc_payment_load_payments($order->order_id);
|
||||
|
||||
if ($payments === FALSE) {
|
||||
return $total;
|
||||
}
|
||||
|
||||
foreach ($payments as $payment) {
|
||||
$total -= $payment->amount;
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a single payment from the database by receipt_id.
|
||||
*/
|
||||
function uc_payment_load($receipt_id) {
|
||||
if (!is_numeric($receipt_id)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$payment = db_query("SELECT * FROM {uc_payment_receipts} WHERE receipt_id = :id", array(':id' => $receipt_id))->fetchObject();
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an array of all the payments for an order.
|
||||
*
|
||||
* @param $order_id
|
||||
* The order's id.
|
||||
*
|
||||
* @return
|
||||
* Array of payment objects or FALSE if there are none.
|
||||
*/
|
||||
function uc_payment_load_payments($order_id) {
|
||||
$payments = db_query("SELECT * FROM {uc_payment_receipts} WHERE order_id = :id ORDER BY received ASC", array(':id' => $order_id))->fetchAll();
|
||||
|
||||
if (count($payments) == 0) {
|
||||
$payments = FALSE;
|
||||
}
|
||||
|
||||
return $payments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of payment methods defined in the enabled modules.
|
||||
*/
|
||||
function _uc_payment_method_list($action = NULL) {
|
||||
static $methods = array();
|
||||
|
||||
if (count($methods) > 0 && $action !== 'rebuild') {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
foreach (module_invoke_all('uc_payment_method') as $id => $method) {
|
||||
// Preserve backward compatibility for methods with no key specified.
|
||||
if (is_numeric($id)) {
|
||||
$id = $method['id'];
|
||||
}
|
||||
|
||||
$methods[$id] = array_merge($method, array(
|
||||
'id' => $id,
|
||||
'checkout' => variable_get('uc_payment_method_' . $id . '_checkout', $method['checkout']),
|
||||
'weight' => variable_get('uc_payment_method_' . $id . '_weight', $method['weight']),
|
||||
));
|
||||
}
|
||||
|
||||
// Allow other modules to alter the payment methods.
|
||||
drupal_alter('uc_payment_method', $methods);
|
||||
|
||||
uasort($methods, 'uc_weight_sort');
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data from a payment method by method ID and the array key.
|
||||
*/
|
||||
function _uc_payment_method_data($method_id, $key) {
|
||||
$methods = _uc_payment_method_list();
|
||||
return isset($methods[$method_id][$key]) ? $methods[$method_id][$key] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of payment gateways defined in the enabled modules.
|
||||
*/
|
||||
function _uc_payment_gateway_list($filter = NULL, $enabled_only = FALSE) {
|
||||
$gateways = array();
|
||||
|
||||
foreach (module_invoke_all('uc_payment_gateway') as $id => $gateway) {
|
||||
// Preserve backward compatibility for gateways with no key specified.
|
||||
if (is_numeric($id)) {
|
||||
$id = $gateway['id'];
|
||||
}
|
||||
|
||||
$gateways[$id] = array_merge($gateway, array(
|
||||
'id' => $id,
|
||||
'enabled' => variable_get('uc_pg_' . $id . '_enabled', TRUE),
|
||||
));
|
||||
}
|
||||
|
||||
// Allow other modules to alter the payment gateways.
|
||||
drupal_alter('uc_payment_gateway', $gateways);
|
||||
|
||||
foreach ($gateways as $id => $gateway) {
|
||||
if ($filter && (!isset($gateway[$filter]) || !function_exists($gateway[$filter]))) {
|
||||
unset($gateways[$id]);
|
||||
continue;
|
||||
}
|
||||
if ($enabled_only && !$gateway['enabled']) {
|
||||
unset($gateways[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
return $gateways;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data from a payment gateway by gateway ID and the array key.
|
||||
*
|
||||
* @param $gateway_id
|
||||
* The ID of the payment gateway to query.
|
||||
* @param $key
|
||||
* The key of the data being requested.
|
||||
*
|
||||
* @return
|
||||
* The requested data.
|
||||
*/
|
||||
function _uc_payment_gateway_data($gateway_id, $key) {
|
||||
$gateways = _uc_payment_gateway_list();
|
||||
return isset($gateways[$gateway_id][$key]) ? $gateways[$gateway_id][$key] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an option list of payment methods.
|
||||
*/
|
||||
function uc_payment_method_options_list() {
|
||||
$options = array();
|
||||
foreach (_uc_payment_method_list() as $id => $method) {
|
||||
$options[$id] = $method['name'];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
function uc_payment_views_api() {
|
||||
return array(
|
||||
'api' => '2.0',
|
||||
'path' => drupal_get_path('module', 'uc_payment') . '/views',
|
||||
);
|
||||
}
|
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules definitions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_rules_event_info().
|
||||
*/
|
||||
function uc_payment_rules_event_info() {
|
||||
$events['uc_payment_entered'] = array(
|
||||
'label' => t('A payment gets entered for an order'),
|
||||
'group' => t('Payment'),
|
||||
'variables' => array(
|
||||
'order' => array(
|
||||
'type' => 'uc_order',
|
||||
'label' => t('Order'),
|
||||
),
|
||||
'account' => array(
|
||||
'type' => 'user',
|
||||
'label' => t('User'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rules_condition_info().
|
||||
*/
|
||||
function uc_payment_rules_condition_info() {
|
||||
$conditions['uc_payment_condition_order_balance'] = array(
|
||||
'label' => t('Check the order balance'),
|
||||
'group' => t('Payment'),
|
||||
'base' => 'uc_payment_condition_order_balance',
|
||||
'parameter' => array(
|
||||
'order' => array(
|
||||
'type' => 'uc_order',
|
||||
'label' => t('Order'),
|
||||
'restriction' => 'selector',
|
||||
),
|
||||
'balance_comparison' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Operator'),
|
||||
'options list' => 'uc_payment_condition_balance_options',
|
||||
'restriction' => 'input',
|
||||
),
|
||||
'include_authorizations' => array(
|
||||
'type' => 'boolean',
|
||||
'label' => t('Include authorizations?'),
|
||||
'description' => t('Should "authorization only" credit card transactions be used in calculating the order balance?'),
|
||||
'restriction' => 'input',
|
||||
'optional' => TRUE,
|
||||
'default value' => FALSE,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition: Check the current order balance.
|
||||
*/
|
||||
function uc_payment_condition_order_balance($order, $balance_comparison, $include_authorizations) {
|
||||
$balance = uc_payment_balance($order);
|
||||
if ($include_authorizations) {
|
||||
foreach ((array) $order->data['cc_txns']['authorizations'] as $auth_id => $data) {
|
||||
$balance -= $data['amount'];
|
||||
}
|
||||
}
|
||||
|
||||
switch ($balance_comparison) {
|
||||
case 'less':
|
||||
return $balance < 0;
|
||||
case 'less_equal':
|
||||
return $balance <= 0.01;
|
||||
case 'equal':
|
||||
return $balance < 0.01 && $balance > -0.01;
|
||||
case 'greater':
|
||||
return $balance >= 0.01;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns balance options.
|
||||
*/
|
||||
function uc_payment_condition_balance_options() {
|
||||
$zero = array('!zero' => uc_currency_format(0));
|
||||
$options = array(
|
||||
'less' => t('Balance is less than !zero.', $zero),
|
||||
'less_equal' => t('Balance is less than or equal to !zero.', $zero),
|
||||
'equal' => t('Balance is equal to !zero.', $zero),
|
||||
'greater' => t('Balance is greater than !zero.', $zero),
|
||||
);
|
||||
|
||||
return $options;
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Default rules configurations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_default_rules_configuration().
|
||||
*/
|
||||
function uc_payment_default_rules_configuration() {
|
||||
$configs = array();
|
||||
|
||||
// Set the order status to "Payment Received" when a payment is received
|
||||
// and the balance is less than or equal to 0.
|
||||
$rule = rules_reaction_rule();
|
||||
$rule->label = t('Update order status on full payment');
|
||||
$rule->active = TRUE;
|
||||
$rule->event('uc_payment_entered')
|
||||
->condition('uc_payment_condition_order_balance', array(
|
||||
'order:select' => 'order',
|
||||
'balance_comparison' => 'less_equal',
|
||||
))
|
||||
->condition(rules_or()
|
||||
->condition('uc_order_condition_order_state', array(
|
||||
'order:select' => 'order',
|
||||
'order_state' => 'in_checkout',
|
||||
))
|
||||
->condition('uc_order_condition_order_state', array(
|
||||
'order:select' => 'order',
|
||||
'order_state' => 'post_checkout',
|
||||
))
|
||||
)
|
||||
->action('uc_order_update_status', array(
|
||||
'order:select' => 'order',
|
||||
'order_status' => 'payment_received',
|
||||
));
|
||||
$configs['uc_payment_received'] = $rule;
|
||||
|
||||
// Set the order status to "Completed" when checkout is complete, none
|
||||
// of the products are shippable, and the balance is less than or equal to 0.
|
||||
$rule = rules_reaction_rule();
|
||||
$rule->label = t('Complete non-shippable order after payment received');
|
||||
$rule->active = TRUE;
|
||||
$rule->event('uc_order_status_update')
|
||||
->condition('data_is', array('data:select' => 'updated_order:order-status', 'value' => 'payment_received'))
|
||||
->condition(rules_condition('uc_order_condition_is_shippable', array(
|
||||
'order:select' => 'updated_order',
|
||||
))
|
||||
->negate())
|
||||
->action('uc_order_update_status', array(
|
||||
'order:select' => 'order',
|
||||
'order_status' => 'completed',
|
||||
));
|
||||
$configs['uc_checkout_complete_paid'] = $rule;
|
||||
|
||||
$methods = _uc_payment_method_list();
|
||||
foreach ($methods as $id => $method) {
|
||||
$set = rules_and(array(
|
||||
'order' => array('type' => 'uc_order', 'label' => t('Order')),
|
||||
));
|
||||
$set->label = t('@method conditions', array('@method' => $method['name']));
|
||||
|
||||
$configs['uc_payment_method_' . $method['id']] = $set;
|
||||
}
|
||||
|
||||
return $configs;
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Theme functions for the uc_payment module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generates markup for payment totals.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_uc_payment_totals($variables) {
|
||||
$order = $variables['order'];
|
||||
$line_items = uc_order_load_line_items_display($order);
|
||||
|
||||
$output = '<table id="uc-order-total-preview">';
|
||||
|
||||
foreach ($line_items as $line) {
|
||||
if (!empty($line['title'])) {
|
||||
$attributes = drupal_attributes(array('class' => array('line-item-' . $line['type'])));
|
||||
$output .= '<tr' . $attributes . '><td class="title">' . filter_xss($line['title']) . ':</td>'
|
||||
. '<td class="price">' . theme('uc_price', array('price' => $line['amount'])) . '</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</table>';
|
||||
|
||||
return $output;
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Token hooks for the uc_payment module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function uc_payment_token_info() {
|
||||
$order['payment-method'] = array(
|
||||
'name' => t('Payment method'),
|
||||
'description' => t('The payment method of the order.'),
|
||||
);
|
||||
$order['payment-balance'] = array(
|
||||
'name' => t('Balance'),
|
||||
'description' => t('The payment balance of the order'),
|
||||
);
|
||||
|
||||
return array(
|
||||
'tokens' => array('uc_order' => $order),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function uc_payment_tokens($type, $tokens, $data = array(), $options = array()) {
|
||||
$values = array();
|
||||
|
||||
if ($type == 'uc_order' && !empty($data['uc_order'])) {
|
||||
$order = $data['uc_order'];
|
||||
|
||||
if (isset($tokens['payment-method'])) {
|
||||
$original = $tokens['payment-method'];
|
||||
$values[$original] = _uc_payment_method_data($order->payment_method, 'review');
|
||||
if (empty($values[$original])) {
|
||||
$values[$original] = _uc_payment_method_data($order->payment_method, 'name');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($tokens['payment-balance'])) {
|
||||
$original = $tokens['payment-balance'];
|
||||
$values[$original] = uc_currency_format(uc_payment_balance($order));
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Checkout pane functions for uc_payment.module.
|
||||
*
|
||||
* The checkout pane holds form to select the payment method. It also shows a
|
||||
* preview of the line items and order total.
|
||||
*/
|
||||
|
||||
function uc_checkout_pane_payment($op, &$order, $form = NULL, &$form_state = NULL) {
|
||||
switch ($op) {
|
||||
case 'view':
|
||||
$contents['#attached']['css'][] = drupal_get_path('module', 'uc_payment') . '/uc_payment.css';
|
||||
|
||||
if (variable_get('uc_payment_show_order_total_preview', TRUE)) {
|
||||
$contents['line_items'] = array(
|
||||
'#theme' => 'uc_payment_totals',
|
||||
'#order' => $order,
|
||||
'#prefix' => '<div id="line-items-div">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -20,
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure that the form builder uses #default_value to determine which
|
||||
// button should be selected after an ajax submission. This is
|
||||
// necessary because the previously selected value may have become
|
||||
// unavailable, which would result in an invalid selection.
|
||||
unset($form_state['input']['panes']['payment']['payment_method']);
|
||||
|
||||
$options = array();
|
||||
foreach (_uc_payment_method_list() as $id => $method) {
|
||||
$set = rules_config_load('uc_payment_method_' . $method['id']);
|
||||
if ($set && !$set->execute($order)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($method['checkout'] && !isset($method['express'])) {
|
||||
$options[$id] = $method['title'];
|
||||
}
|
||||
}
|
||||
|
||||
drupal_alter('uc_payment_method_checkout', $options, $order);
|
||||
|
||||
$description = '';
|
||||
if (!$options) {
|
||||
$description = t('Checkout cannot be completed without any payment methods enabled. Please contact an administrator to resolve the issue.');
|
||||
$options[''] = t('No payment methods available');
|
||||
}
|
||||
elseif (count($options) > 1) {
|
||||
$description = t('Select a payment method from the following options.');
|
||||
}
|
||||
|
||||
if (!isset($options[$order->payment_method])) {
|
||||
$order->payment_method = key($options);
|
||||
}
|
||||
|
||||
$contents['payment_method'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Payment method'),
|
||||
'#title_display' => 'invisible',
|
||||
'#options' => $options,
|
||||
'#default_value' => $order->payment_method,
|
||||
'#disabled' => count($options) == 1,
|
||||
'#required' => TRUE,
|
||||
'#ajax' => array(
|
||||
'callback' => 'uc_payment_checkout_payment_details',
|
||||
'wrapper' => 'payment-details',
|
||||
'progress' => array(
|
||||
'type' => 'throbber',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$contents['details'] = array(
|
||||
'#prefix' => '<div id="payment-details" class="clearfix payment-details-' . $order->payment_method . '">',
|
||||
'#markup' => t('Continue with checkout to complete payment.'),
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func) && $details = $func('cart-details', $order, $form, $form_state)) {
|
||||
unset($contents['details']['#markup']);
|
||||
$contents['details'] += $details;
|
||||
}
|
||||
|
||||
return array('description' => $description, 'contents' => $contents);
|
||||
|
||||
case 'process':
|
||||
if (empty($form_state['values']['panes']['payment']['payment_method'])) {
|
||||
form_set_error('panes][payment][payment_method', t('You cannot check out without selecting a payment method.'));
|
||||
return FALSE;
|
||||
}
|
||||
$order->payment_method = $form_state['values']['panes']['payment']['payment_method'];
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func)) {
|
||||
$result = $func('cart-process', $order, $form, $form_state);
|
||||
if ($result === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case 'review':
|
||||
$line_items = uc_order_load_line_items_display($order);
|
||||
foreach ($line_items as $line_item) {
|
||||
$review[] = array('title' => $line_item['title'], 'data' => theme('uc_price', array('price' => $line_item['amount'])));
|
||||
}
|
||||
$review_data = _uc_payment_method_data($order->payment_method, 'review');
|
||||
if (empty($review_data)) {
|
||||
$review_data = _uc_payment_method_data($order->payment_method, 'name');
|
||||
}
|
||||
$review[] = array('border' => 'top', 'title' => t('Paying by'), 'data' => $review_data);
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func)) {
|
||||
$result = $func('cart-review', $order);
|
||||
if (is_array($result)) {
|
||||
$review = array_merge($review, $result);
|
||||
}
|
||||
}
|
||||
return $review;
|
||||
|
||||
case 'settings':
|
||||
$form['uc_payment_show_order_total_preview'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Show the order total preview on the payment pane.'),
|
||||
'#default_value' => variable_get('uc_payment_show_order_total_preview', TRUE),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback for payment method details on the checkout form.
|
||||
*/
|
||||
function uc_payment_checkout_payment_details($form, $form_state) {
|
||||
return $form['panes']['payment']['details'];
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the callbacks for the payment order pane supplied with
|
||||
* Ubercart and their corresponding helper functions.
|
||||
*
|
||||
* Order panes are defined using hook_uc_order_pane() and use a callback to
|
||||
* handle the different processes involved in order viewing/editing. The
|
||||
* payment order pane is defined in uc_payment_uc_order_pane() in
|
||||
* uc_payment.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles the Payment order pane.
|
||||
*/
|
||||
function uc_order_pane_payment($op, $order, &$form = NULL, &$form_state = NULL) {
|
||||
switch ($op) {
|
||||
case 'view':
|
||||
$build['balance'] = array('#markup' => t('Balance: @balance', array('@balance' => uc_currency_format(uc_payment_balance($order)))));
|
||||
|
||||
if (user_access('view payments')) {
|
||||
$build['view_payments'] = array(
|
||||
'#markup' => ' (' . l(t('View'), 'admin/store/orders/' . $order->order_id . '/payments') . ')',
|
||||
);
|
||||
}
|
||||
|
||||
$method_name = _uc_payment_method_data($order->payment_method, 'review');
|
||||
if (empty($method_name)) {
|
||||
$method_name = _uc_payment_method_data($order->payment_method, 'name');
|
||||
}
|
||||
$build['method'] = array(
|
||||
'#markup' => t('Method: @payment_method', array('@payment_method' => $method_name)),
|
||||
'#prefix' => '<br />',
|
||||
);
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func)) {
|
||||
$method_output = $func('order-view', $order);
|
||||
if (!empty($method_output)) {
|
||||
$build['output'] = $method_output + array(
|
||||
'#prefix' => '<br />',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $build;
|
||||
|
||||
case 'customer':
|
||||
$method_name = _uc_payment_method_data($order->payment_method, 'review');
|
||||
if (empty($method_name)) {
|
||||
$method_name = _uc_payment_method_data($order->payment_method, 'name');
|
||||
}
|
||||
$build['method'] = array('#markup' => t('Method: @payment_method', array('@payment_method' => $method_name)));
|
||||
$func = _uc_payment_method_data($order->payment_method, 'callback');
|
||||
if (function_exists($func)) {
|
||||
$method_output = $func('customer-view', $order);
|
||||
if (!empty($method_output)) {
|
||||
$build['output'] = $method_output + array(
|
||||
'#prefix' => '<br />',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $build;
|
||||
|
||||
case 'edit-form':
|
||||
$methods = _uc_payment_method_list();
|
||||
$options = array();
|
||||
foreach ($methods as $id => $method) {
|
||||
$options[$id] = $method['name'];
|
||||
}
|
||||
$form['payment']['payment_method'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Payment method'),
|
||||
'#default_value' => $order->payment_method,
|
||||
'#options' => !empty($options) ? $options : array(t('None available')),
|
||||
'#disabled' => empty($options),
|
||||
'#ajax' => array(
|
||||
'callback' => 'uc_payment_order_pane_ajax_callback',
|
||||
'progress' => array('type' => 'throbber'),
|
||||
'wrapper' => 'payment-details',
|
||||
),
|
||||
);
|
||||
|
||||
$form['payment']['payment_details'] = array(
|
||||
'#tree' => TRUE,
|
||||
'#prefix' => '<div id="payment-details">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
|
||||
$method = isset($form_state['values']['payment_method']) ? $form_state['values']['payment_method'] : $order->payment_method;
|
||||
$func = _uc_payment_method_data($method, 'callback');
|
||||
if (function_exists($func) && $details = $func('order-details', $order)) {
|
||||
if (is_array($details)) {
|
||||
$form['payment']['payment_details'] += $details;
|
||||
}
|
||||
else {
|
||||
$form['payment']['payment_details']['#markup'] = $details;
|
||||
}
|
||||
}
|
||||
return $form;
|
||||
|
||||
case 'edit-theme':
|
||||
return drupal_render($form['payment']);
|
||||
|
||||
case 'edit-process':
|
||||
$changes['payment_method'] = $form_state['values']['payment_method'];
|
||||
$changes['payment_details'] = isset($form_state['values']['payment_details']) ? $form_state['values']['payment_details'] : array();
|
||||
$func = _uc_payment_method_data($form_state['values']['payment_method'], 'callback');
|
||||
if (function_exists($func) && ($return = $func('edit-process', $order, $form, $form_state)) != NULL && is_array($return)) {
|
||||
$changes['payment_details'] = array_merge($changes['payment_details'], $return);
|
||||
}
|
||||
|
||||
if (!isset($order->payment_details)) {
|
||||
$order->payment_details = array();
|
||||
}
|
||||
return $changes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback to render the payment method pane.
|
||||
*/
|
||||
function uc_payment_order_pane_ajax_callback($form, &$form_state) {
|
||||
$commands[] = ajax_command_replace('#payment-details', trim(drupal_render($form['payment']['payment_details'])));
|
||||
$commands[] = ajax_command_prepend('#payment-details', theme('status_messages'));
|
||||
return array('#type' => 'ajax', '#commands' => $commands);
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Views hooks and callback registries.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function uc_payment_views_data() {
|
||||
$data['uc_orders']['payments'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Payments'),
|
||||
'help' => t('Relate payments to an order. This relationship will create one record for each payment received.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_payment_receipts',
|
||||
'base field' => 'order_id',
|
||||
'relationship field' => 'order_id',
|
||||
'label' => t('payments'),
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_payment_receipts']['table']['group'] = t('Payment');
|
||||
|
||||
$data['uc_payment_receipts']['method'] = array(
|
||||
'title' => t('Payment method'),
|
||||
'help' => t('The method of payment.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_payment_receipts']['amount'] = array(
|
||||
'title' => t('Amount'),
|
||||
'help' => t('The amount paid.'),
|
||||
'field' => array(
|
||||
'handler' => 'uc_order_handler_field_money_amount',
|
||||
'click sortable' => TRUE,
|
||||
'float' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_payment_receipts']['uid'] = array(
|
||||
'title' => t('User'),
|
||||
'help' => t('Relate a payment to the user who made it.'),
|
||||
'relationship' => array(
|
||||
'base' => 'users',
|
||||
'field' => 'uid',
|
||||
'handler' => 'views_handler_relationship',
|
||||
'label' => t('user'),
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_payment_receipts']['comment'] = array(
|
||||
'title' => t('Comment'),
|
||||
'help' => t('Any remarks that were included with the payment.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_xss',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_payment_receipts']['received'] = array(
|
||||
'title' => t('Receipt date'),
|
||||
'help' => t('The date and time the payment was received.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Payment pack administration menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Receives a check for an order and put in a clear date.
|
||||
*
|
||||
* @see uc_payment_pack_receive_check_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_payment_pack_receive_check_form($form, &$form_state, $order) {
|
||||
$balance = uc_payment_balance($order);
|
||||
$form['balance'] = array(
|
||||
'#prefix' => '<strong>' . t('Order balance:') . '</strong> ',
|
||||
'#markup' => uc_currency_format($balance),
|
||||
);
|
||||
$form['order_id'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#value' => $order->order_id,
|
||||
);
|
||||
$form['amount'] = array(
|
||||
'#type' => 'uc_price',
|
||||
'#title' => t('Amount'),
|
||||
'#default_value' => $balance,
|
||||
);
|
||||
$form['comment'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Comment'),
|
||||
'#description' => t('Any notes about the check, like type or check number.'),
|
||||
'#size' => 64,
|
||||
'#maxlength' => 256,
|
||||
);
|
||||
$form['clear'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Expected clear date'),
|
||||
'#attributes' => array('class' => array('uc-inline-form', 'clearfix')),
|
||||
);
|
||||
$form['clear']['clear_month'] = uc_select_month(NULL, format_date(REQUEST_TIME, 'custom', 'n'));
|
||||
$form['clear']['clear_day'] = uc_select_day(NULL, format_date(REQUEST_TIME, 'custom', 'j'));
|
||||
$form['clear']['clear_year'] = uc_select_year(NULL, format_date(REQUEST_TIME, 'custom', 'Y'), format_date(REQUEST_TIME, 'custom', 'Y'), format_date(REQUEST_TIME, 'custom', 'Y') + 1);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Receive check'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for uc_payment_pack_receive_check_form().
|
||||
*
|
||||
* @see uc_payment_pack_receive_check_form()
|
||||
*/
|
||||
function uc_payment_pack_receive_check_form_submit($form, &$form_state) {
|
||||
global $user;
|
||||
|
||||
uc_payment_enter($form_state['values']['order_id'], 'check', $form_state['values']['amount'], $user->uid, '', $form_state['values']['comment']);
|
||||
|
||||
db_insert('uc_payment_check')
|
||||
->fields(array(
|
||||
'order_id' => $form_state['values']['order_id'],
|
||||
'clear_date' => mktime(12, 0, 0, $form_state['values']['clear_month'], $form_state['values']['clear_day'], $form_state['values']['clear_year']),
|
||||
))
|
||||
->execute();
|
||||
|
||||
drupal_set_message(t('Check received, expected clear date of @date.', array('@date' => uc_date_format($form_state['values']['clear_month'], $form_state['values']['clear_day'], $form_state['values']['clear_year']))));
|
||||
|
||||
$form_state['redirect'] = 'admin/store/orders/' . $form_state['values']['order_id'];
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
name = Payment method pack
|
||||
description = Provides the check/money order, COD, and 'other' payment methods.
|
||||
dependencies[] = uc_cart
|
||||
dependencies[] = uc_payment
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_payment_pack module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_payment_pack_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_payment_check'] = array(
|
||||
'description' => 'Stores check payment information.',
|
||||
'fields' => array(
|
||||
'check_id' => array(
|
||||
'description' => 'Primary key: the check ID.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'clear_date' => array(
|
||||
'description' => 'The Unix timestamp indicating the expected clear date for the check.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'order_id' => array('order_id'),
|
||||
),
|
||||
'primary key' => array('check_id'),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_payment_cod'] = array(
|
||||
'description' => 'Stores COD payment information.',
|
||||
'fields' => array(
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'delivery_month' => array(
|
||||
'description' => 'The month of delivery. 1 => January, 2 => February, etc.',
|
||||
'type' => 'int',
|
||||
'size' => 'small',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'delivery_day' => array(
|
||||
'description' => 'The day of the month of delivery.',
|
||||
'type' => 'int',
|
||||
'size' => 'small',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'delivery_year' => array(
|
||||
'description' => 'The year of delivery.',
|
||||
'type' => 'int',
|
||||
'size' => 'small',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('order_id'),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_payment_other'] = array(
|
||||
'description' => 'Stores Other payment type information.',
|
||||
'fields' => array(
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => 'The description of the payment type.',
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
),
|
||||
'primary key' => array('order_id'),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_payment_pack_uninstall() {
|
||||
db_delete('variable')
|
||||
->condition(db_or()
|
||||
->condition('name', 'uc_check_%', 'LIKE')
|
||||
->condition('name', 'uc_cod_%', 'LIKE')
|
||||
)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_payment_pack_update_last_removed() {
|
||||
return 6000;
|
||||
}
|
@@ -0,0 +1,395 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the Check/Money Order, COD, and "Other" payment methods.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function uc_payment_pack_menu() {
|
||||
$items['admin/store/orders/%uc_order/receive_check'] = array(
|
||||
'title' => 'Receive check',
|
||||
'description' => 'Record details of a check that has been received.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_payment_pack_receive_check_form', 3),
|
||||
'access arguments' => array('view all orders'),
|
||||
'file' => 'uc_payment_pack.admin.inc',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_payment_method().
|
||||
*/
|
||||
function uc_payment_pack_uc_payment_method() {
|
||||
$methods['check'] = array(
|
||||
'name' => t('Check', array(), array('context' => 'cheque')),
|
||||
'title' => t('Check or money order'),
|
||||
'desc' => t('Pay by mailing a check or money order.'),
|
||||
'callback' => 'uc_payment_method_check',
|
||||
'weight' => 1,
|
||||
'checkout' => TRUE,
|
||||
'no_gateway' => TRUE,
|
||||
);
|
||||
$methods['cod'] = array(
|
||||
'name' => t('COD'),
|
||||
'title' => t('Cash on delivery'),
|
||||
'desc' => t('Pay cash on delivery on pick-up.'),
|
||||
'callback' => 'uc_payment_method_cod',
|
||||
'weight' => 1,
|
||||
'checkout' => FALSE,
|
||||
'no_gateway' => TRUE,
|
||||
);
|
||||
$methods['other'] = array(
|
||||
'name' => t('Other'),
|
||||
'title' => t('Other'),
|
||||
'desc' => t('A generic payment method type.'),
|
||||
'callback' => 'uc_payment_method_other',
|
||||
'weight' => 10,
|
||||
'checkout' => FALSE,
|
||||
'no_gateway' => TRUE,
|
||||
);
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment method callback for the generic payment method "Other".
|
||||
*/
|
||||
function uc_payment_method_other($op, &$order) {
|
||||
switch ($op) {
|
||||
case 'order-view':
|
||||
case 'customer-view':
|
||||
// Fetch the description for the payment entered by the administrator.
|
||||
if ($description = db_query('SELECT description FROM {uc_payment_other} WHERE order_id = :id', array(':id' => $order->order_id))->fetchField()) {
|
||||
return array('#markup' => t('Description: @desc', array('@desc' => $description)));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'order-details':
|
||||
$form['pm_other_description'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Description'),
|
||||
'#default_value' => isset($order->payment_details['description']) ? $order->payment_details['description'] : '',
|
||||
'#size' => 32,
|
||||
'#maxlength' => 64,
|
||||
);
|
||||
return $form;
|
||||
|
||||
case 'order-load':
|
||||
$description = db_query('SELECT description FROM {uc_payment_other} WHERE order_id = :id', array(':id' => $order->order_id))->fetchField();
|
||||
if (isset($description)) {
|
||||
$order->payment_details['description'] = $description;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'order-save':
|
||||
if (empty($order->payment_details['pm_other_description'])) {
|
||||
db_delete('uc_payment_other')
|
||||
->condition('order_id', $order->order_id)
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
db_merge('uc_payment_other')
|
||||
->key(array(
|
||||
'order_id' => $order->order_id,
|
||||
))
|
||||
->fields(array(
|
||||
'description' => $order->payment_details['pm_other_description'],
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment method callback for the "Cash on Delivery" payment method.
|
||||
*/
|
||||
function uc_payment_method_cod($op, &$order, $form = NULL, $form_state = NULL) {
|
||||
switch ($op) {
|
||||
case 'cart-details':
|
||||
$build['policy'] = array(
|
||||
'#markup' => '<p>' . variable_get('uc_cod_policy', t('Full payment is expected upon delivery or prior to pick-up.')) . '</p>'
|
||||
);
|
||||
|
||||
if (($max = variable_get('uc_cod_max_order', 0)) > 0 && is_numeric($max)) {
|
||||
$build['eligibility'] = array(
|
||||
'#markup' => '<p>' . t('Orders totalling more than !number are <b>not eligible</b> for COD.', array('!number' => uc_currency_format($max))) . '</p>'
|
||||
);
|
||||
}
|
||||
|
||||
if (variable_get('uc_cod_delivery_date', FALSE)) {
|
||||
$build += uc_payment_method_cod_form(array(), $form_state, $order);
|
||||
}
|
||||
return $build;
|
||||
|
||||
case 'cart-process':
|
||||
if (variable_get('uc_cod_delivery_date', FALSE)) {
|
||||
$order->payment_details = $form_state['values']['panes']['payment']['details'];
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case 'cart-review':
|
||||
$review = array();
|
||||
if (variable_get('uc_cod_delivery_date', FALSE)) {
|
||||
$date = uc_date_format(
|
||||
$order->payment_details['delivery_month'],
|
||||
$order->payment_details['delivery_day'],
|
||||
$order->payment_details['delivery_year']
|
||||
);
|
||||
$review[] = array('title' => t('Delivery date'), 'data' => $date);
|
||||
}
|
||||
return $review;
|
||||
|
||||
case 'order-view':
|
||||
case 'customer-view':
|
||||
$build = array('#markup' => '');
|
||||
if (variable_get('uc_cod_delivery_date', FALSE) &&
|
||||
isset($order->payment_details['delivery_month']) &&
|
||||
isset($order->payment_details['delivery_day']) &&
|
||||
isset($order->payment_details['delivery_year'])) {
|
||||
$build['#markup'] = t('Desired delivery date:') . '<br />' .
|
||||
uc_date_format(
|
||||
$order->payment_details['delivery_month'],
|
||||
$order->payment_details['delivery_day'],
|
||||
$order->payment_details['delivery_year']
|
||||
);
|
||||
}
|
||||
return $build;
|
||||
|
||||
case 'order-details':
|
||||
$build = array();
|
||||
if (variable_get('uc_cod_delivery_date', FALSE)) {
|
||||
$build = uc_payment_method_cod_form(array(), $form_state, $order);
|
||||
}
|
||||
return $build;
|
||||
|
||||
case 'order-load':
|
||||
$result = db_query('SELECT * FROM {uc_payment_cod} WHERE order_id = :id', array(':id' => $order->order_id));
|
||||
if ($row = $result->fetchObject()) {
|
||||
$order->payment_details = array(
|
||||
'delivery_month' => $row->delivery_month,
|
||||
'delivery_day' => $row->delivery_day,
|
||||
'delivery_year' => $row->delivery_year,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'order-submit':
|
||||
if ($order->payment_method == 'cod' &&
|
||||
($max = variable_get('uc_cod_max_order', 0)) > 0 &&
|
||||
is_numeric($max) &&
|
||||
$order->order_total > $max) {
|
||||
$result[] = array(
|
||||
'pass' => FALSE,
|
||||
'message' => t('Your final order total exceeds the maximum for COD payment. Please go back and select a different method of payment.')
|
||||
);
|
||||
$_SESSION['expanded_panes'][] = 'payment';
|
||||
return $result;
|
||||
}
|
||||
// TODO: This falls through to the order-save case - is this deliberate?
|
||||
// If so, it should be documented.
|
||||
|
||||
case 'order-save':
|
||||
if (isset($order->payment_details['delivery_month']) &&
|
||||
isset($order->payment_details['delivery_day']) &&
|
||||
isset($order->payment_details['delivery_year']) ) {
|
||||
db_merge('uc_payment_cod')
|
||||
->key(array('order_id' => $order->order_id))
|
||||
->fields(array(
|
||||
'delivery_month' => $order->payment_details['delivery_month'],
|
||||
'delivery_day' => $order->payment_details['delivery_day'],
|
||||
'delivery_year' => $order->payment_details['delivery_year'],
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'order-delete':
|
||||
db_delete('uc_payment_cod')
|
||||
->condition('order_id', $order->order_id)
|
||||
->execute();
|
||||
break;
|
||||
|
||||
case 'settings':
|
||||
$form['uc_cod_policy'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Policy message'),
|
||||
'#default_value' => variable_get('uc_cod_policy', t('Full payment is expected upon delivery or prior to pick-up.')),
|
||||
'#description' => t('Help message shown at checkout.'),
|
||||
);
|
||||
$form['uc_cod_max_order'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Maximum order total eligible for COD'),
|
||||
'#default_value' => variable_get('uc_cod_max_order', 0),
|
||||
'#description' => t('Set to 0 for no maximum order limit.'),
|
||||
);
|
||||
$form['uc_cod_delivery_date'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Let customers enter a desired delivery date.'),
|
||||
'#default_value' => variable_get('uc_cod_delivery_date', FALSE),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect additional information for the "Cash on Delivery" payment method.
|
||||
*
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_payment_method_cod_form($form, &$form_state, $order) {
|
||||
$month = !empty($order->payment_details['delivery_month']) ? $order->payment_details['delivery_month'] : format_date(REQUEST_TIME, 'custom', 'n');
|
||||
$day = !empty($order->payment_details['delivery_day']) ? $order->payment_details['delivery_day'] : format_date(REQUEST_TIME, 'custom', 'j');
|
||||
$year = !empty($order->payment_details['delivery_year']) ? $order->payment_details['delivery_year'] : format_date(REQUEST_TIME, 'custom', 'Y');
|
||||
|
||||
$form['description'] = array(
|
||||
'#markup' => '<div>' . t('Enter a desired delivery date:') . '</div>',
|
||||
);
|
||||
$form['delivery_month'] = uc_select_month(NULL, $month);
|
||||
$form['delivery_day'] = uc_select_day(NULL, $day);
|
||||
$form['delivery_year'] = uc_select_year(NULL, $year, format_date(REQUEST_TIME, 'custom', 'Y'), format_date(REQUEST_TIME, 'custom', 'Y') + 1);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment method callback for the "Check" payment method.
|
||||
*/
|
||||
function uc_payment_method_check($op, &$order, $form = NULL, &$form_state = NULL) {
|
||||
switch ($op) {
|
||||
case 'cart-details':
|
||||
$build['instructions'] = array(
|
||||
'#markup' => t('Checks should be made out to:')
|
||||
);
|
||||
|
||||
if (!variable_get('uc_check_mailing_street1', FALSE)) {
|
||||
$build['address'] = array(
|
||||
'#markup' => uc_address_format(
|
||||
uc_store_name(),
|
||||
NULL,
|
||||
variable_get('uc_store_company', ''),
|
||||
variable_get('uc_store_street1', ''),
|
||||
variable_get('uc_store_street2', ''),
|
||||
variable_get('uc_store_city', ''),
|
||||
variable_get('uc_store_zone', ''),
|
||||
variable_get('uc_store_postal_code', ''),
|
||||
variable_get('uc_store_country', 840)
|
||||
),
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
);
|
||||
}
|
||||
else {
|
||||
$build['address'] = array(
|
||||
'#markup' => uc_address_format(
|
||||
variable_get('uc_check_mailing_name', ''),
|
||||
NULL,
|
||||
variable_get('uc_check_mailing_company', ''),
|
||||
variable_get('uc_check_mailing_street1', ''),
|
||||
variable_get('uc_check_mailing_street2', ''),
|
||||
variable_get('uc_check_mailing_city', ''),
|
||||
variable_get('uc_check_mailing_zone', ''),
|
||||
variable_get('uc_check_mailing_postal_code', ''),
|
||||
variable_get('uc_check_mailing_country', 840)
|
||||
),
|
||||
'#prefix' => '<p>',
|
||||
'#suffix' => '</p>',
|
||||
);
|
||||
}
|
||||
|
||||
$build['policy'] = array(
|
||||
'#markup' => '<p>' . variable_get('uc_check_policy', '') . '</p>'
|
||||
);
|
||||
return $build;
|
||||
|
||||
case 'cart-review':
|
||||
if (!variable_get('uc_check_mailing_street1', FALSE)) {
|
||||
$review[] = array(
|
||||
'title' => t('Mail to'),
|
||||
'data' => uc_address_format(
|
||||
uc_store_name(),
|
||||
NULL,
|
||||
variable_get('uc_store_company', ''),
|
||||
variable_get('uc_store_street1', ''),
|
||||
variable_get('uc_store_street2', ''),
|
||||
variable_get('uc_store_city', ''),
|
||||
variable_get('uc_store_zone', ''),
|
||||
variable_get('uc_store_postal_code', ''),
|
||||
variable_get('uc_store_country', 840)
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$review[] = array(
|
||||
'title' => t('Mail to'),
|
||||
'data' => uc_address_format(
|
||||
variable_get('uc_check_mailing_name', ''),
|
||||
NULL,
|
||||
variable_get('uc_check_mailing_company', ''),
|
||||
variable_get('uc_check_mailing_street1', ''),
|
||||
variable_get('uc_check_mailing_street2', ''),
|
||||
variable_get('uc_check_mailing_city', ''),
|
||||
variable_get('uc_check_mailing_zone', ''),
|
||||
variable_get('uc_check_mailing_postal_code', ''),
|
||||
variable_get('uc_check_mailing_country', 840)
|
||||
)
|
||||
);
|
||||
}
|
||||
return $review;
|
||||
|
||||
case 'order-view':
|
||||
$build = array('#suffix' => '<br />');
|
||||
|
||||
$result = db_query('SELECT clear_date FROM {uc_payment_check} WHERE order_id = :id ', array(':id' => $order->order_id));
|
||||
if ($clear_date = $result->fetchField()) {
|
||||
$build['#markup'] = t('Clear Date:') . ' ' . format_date($clear_date, 'uc_store');
|
||||
}
|
||||
else {
|
||||
$build['#markup'] = l(t('Receive Check'), 'admin/store/orders/' . $order->order_id . '/receive_check');
|
||||
}
|
||||
return $build;
|
||||
|
||||
case 'customer-view':
|
||||
$build = array();
|
||||
$result = db_query('SELECT clear_date FROM {uc_payment_check} WHERE order_id = :id ', array(':id' => $order->order_id));
|
||||
if ($clear_date = $result->fetchField()) {
|
||||
$build['#markup'] = t('Check received') . '<br />' .
|
||||
t('Expected clear date:') . '<br />' . format_date($clear_date, 'uc_store');
|
||||
}
|
||||
return $build;
|
||||
|
||||
case 'settings':
|
||||
$form['check_address_info'] = array(
|
||||
'#markup' => '<div>' . t('Set the mailing address to display to customers who choose this payment method during checkout.') . '</div>',
|
||||
);
|
||||
$form['uc_check_mailing_name'] = uc_textfield(t('Contact'), variable_get('uc_check_mailing_name', ''), FALSE, t('Direct checks to a person or department.'), 128);
|
||||
$form['uc_check_address'] = array(
|
||||
'#type' => 'uc_address',
|
||||
'#default_value' => array(
|
||||
'uc_check_mailing_company' => variable_get('uc_check_mailing_company', ''),
|
||||
'uc_check_mailing_street1' => variable_get('uc_check_mailing_street1', ''),
|
||||
'uc_check_mailing_street2' => variable_get('uc_check_mailing_street2', ''),
|
||||
'uc_check_mailing_city' => variable_get('uc_check_mailing_city', ''),
|
||||
'uc_check_mailing_zone' => variable_get('uc_check_mailing_zone', ''),
|
||||
'uc_check_mailing_country' => isset($form_state['values']['uc_check_mailing_country']) ? $form_state['values']['uc_check_mailing_country'] : variable_get('uc_check_mailing_country', ''),
|
||||
'uc_check_mailing_postal_code' => variable_get('uc_check_mailing_postal_code', ''),
|
||||
),
|
||||
'#required' => FALSE,
|
||||
'#key_prefix' => 'uc_check_mailing',
|
||||
);
|
||||
$form['uc_check_policy'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Check payment policy', array(), array('context' => 'cheque')),
|
||||
'#description' => t('Instructions for customers on the checkout page.'),
|
||||
'#default_value' => variable_get('uc_check_policy', t('Personal and business checks will be held for up to 10 business days to ensure payment clears before an order is shipped.')),
|
||||
'#rows' => 3,
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Variable module hook implementations.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_variable_group_info().
|
||||
*/
|
||||
function uc_payment_pack_variable_group_info() {
|
||||
$groups['uc_payment_pack'] = array(
|
||||
'title' => t('Ubercart payment pack settings'),
|
||||
'access' => 'administer store',
|
||||
'path' => array('admin/store/settings/payment/method'),
|
||||
);
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_variable_info().
|
||||
*/
|
||||
function uc_payment_pack_variable_info($options) {
|
||||
$variables['uc_cod_policy'] = array(
|
||||
'type' => 'text',
|
||||
'title' => t('COD policy message', array(), $options),
|
||||
'description' => t('Policy message for Cash On Delivery', array(), $options),
|
||||
'group' => 'uc_payment_pack',
|
||||
'default' => t('Full payment is expected upon delivery or prior to pick-up.', array(), $options),
|
||||
);
|
||||
$variables['uc_check_policy'] = array(
|
||||
'type' => 'text',
|
||||
'title' => t('Check payment policy', array(), $options),
|
||||
'description' => t('Instructions for customers on the checkout page.', array(), $options),
|
||||
'group' => 'uc_payment_pack',
|
||||
'default' => t('Personal and business checks will be held for up to 10 business days to ensure payment clears before an order is shipped.', array(), $options),
|
||||
);
|
||||
return $variables;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = PayPal
|
||||
description = Processes payments using PayPal Website Payments Standard, Website Payments Pro and Express Checkout.
|
||||
dependencies[] = uc_payment
|
||||
package = Ubercart - payment
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_paypal module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function uc_paypal_requirements($phase) {
|
||||
$requirements = array();
|
||||
$t = get_t();
|
||||
|
||||
$has_curl = function_exists('curl_init');
|
||||
|
||||
// PayPal WPP requires cURL.
|
||||
if (variable_get('uc_pg_paypal_wpp_enabled', TRUE)) {
|
||||
$requirements['uc_paypal_curl'] = array(
|
||||
'title' => $t('cURL'),
|
||||
'value' => $has_curl ? $t('Enabled') : $t('Not found'),
|
||||
);
|
||||
if (!$has_curl) {
|
||||
$requirements['uc_paypal_curl']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['uc_paypal_curl']['description'] = $t("PayPal WPP requires the PHP <a href='!curl_url'>cURL</a> library.", array('!curl_url' => 'http://php.net/manual/en/curl.setup.php'));
|
||||
}
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_paypal_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_payment_paypal_ipn'] = array(
|
||||
'description' => 'Logs PayPal Instant Payment Notifications.',
|
||||
'fields' => array(
|
||||
'order_id' => array(
|
||||
'description' => 'The order ID.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'txn_id' => array(
|
||||
'description' => 'The transaction ID from PayPal.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'txn_type' => array(
|
||||
'description' => 'The transaction type from PayPal.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'mc_gross' => array(
|
||||
'description' => 'The payment amount from PayPal.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'The IPN status from PayPal.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'receiver_email' => array(
|
||||
'description' => 'The e-mail address of the PayPal account.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'payer_email' => array(
|
||||
'description' => 'The e-mail address of the buyer.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'received' => array(
|
||||
'description' => 'The IPN receipt timestamp.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'order_id' => array('order_id'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function uc_paypal_install() {
|
||||
$t = get_t();
|
||||
|
||||
db_merge('uc_order_statuses')
|
||||
->key(array('order_status_id' => 'paypal_pending'))
|
||||
->insertFields(array(
|
||||
'order_status_id' => 'paypal_pending',
|
||||
'title' => $t('PayPal pending'),
|
||||
'state' => 'post_checkout',
|
||||
'weight' => 7,
|
||||
'locked' => 1,
|
||||
))
|
||||
->updateFields(array(
|
||||
'state' => 'post_checkout',
|
||||
'locked' => 1,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_paypal_uninstall() {
|
||||
db_update('uc_order_statuses')
|
||||
->fields(array(
|
||||
'locked' => 0,
|
||||
))
|
||||
->condition('order_status_id', 'paypal_pending')
|
||||
->execute();
|
||||
|
||||
db_delete('variable')
|
||||
->condition('name', 'uc_paypal_%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_paypal_update_last_removed() {
|
||||
// 7.x-3.0-beta2 and earlier were installed with schema version 0,
|
||||
// which causes update.php to fail.
|
||||
return drupal_get_installed_schema_version('uc_paypal') == 0 ? 0 : 6000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removed completely unnecessary update 7000.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fix incorrect order status configuration.
|
||||
*/
|
||||
function uc_paypal_update_7001() {
|
||||
db_delete('uc_order_statuses')
|
||||
->condition('order_status_id', '')
|
||||
->execute();
|
||||
|
||||
db_merge('uc_order_statuses')
|
||||
->key(array('order_status_id' => 'paypal_pending'))
|
||||
->insertFields(array(
|
||||
'order_status_id' => 'paypal_pending',
|
||||
'title' => t('PayPal pending'),
|
||||
'state' => 'payment_received',
|
||||
'weight' => 7,
|
||||
'locked' => 1,
|
||||
))
|
||||
->updateFields(array(
|
||||
'state' => 'payment_received',
|
||||
'locked' => 1,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix incorrect order state configuration.
|
||||
*/
|
||||
function uc_paypal_update_7300() {
|
||||
db_update('uc_order_statuses')
|
||||
->fields(array(
|
||||
'state' => 'post_checkout',
|
||||
))
|
||||
->condition('order_status_id', 'paypal_pending')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unused variable.
|
||||
*/
|
||||
function uc_paypal_update_7301() {
|
||||
variable_del('uc_paypal_wps_checkout_button');
|
||||
}
|
@@ -0,0 +1,447 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Paypal administration menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Processes Instant Payment Notifiations from PayPal.
|
||||
*/
|
||||
function uc_paypal_ipn() {
|
||||
if (!isset($_POST['invoice'])) {
|
||||
watchdog('uc_paypal', 'IPN attempted with invalid order ID.', array(), WATCHDOG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($_POST['invoice'], '-') > 0) {
|
||||
list($order_id, $cart_id) = explode('-', $_POST['invoice']);
|
||||
|
||||
// Sanitize order ID and cart ID
|
||||
$order_id = intval($order_id);
|
||||
$cart_id = check_plain($cart_id);
|
||||
|
||||
if (!empty($cart_id)) {
|
||||
// Needed later by uc_complete_sale to empty the correct cart
|
||||
$_SESSION['uc_cart_id'] = $cart_id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$order_id = intval($_POST['invoice']);
|
||||
}
|
||||
|
||||
watchdog('uc_paypal', 'Receiving IPN at URL for order @order_id. <pre>@debug</pre>', array('@order_id' => $order_id, '@debug' => variable_get('uc_paypal_wps_debug_ipn', FALSE) ? print_r($_POST, TRUE) : ''));
|
||||
|
||||
$order = uc_order_load($order_id);
|
||||
|
||||
if ($order == FALSE) {
|
||||
watchdog('uc_paypal', 'IPN attempted for non-existent order @order_id.', array('@order_id' => $order_id), WATCHDOG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign posted variables to local variables
|
||||
$payment_status = check_plain($_POST['payment_status']);
|
||||
$payment_amount = check_plain($_POST['mc_gross']);
|
||||
$payment_currency = check_plain($_POST['mc_currency']);
|
||||
$receiver_email = check_plain($_POST['business']);
|
||||
if ($receiver_email == '') {
|
||||
$receiver_email = check_plain($_POST['receiver_email']);
|
||||
}
|
||||
$txn_id = check_plain($_POST['txn_id']);
|
||||
$txn_type = check_plain($_POST['txn_type']);
|
||||
$payer_email = check_plain($_POST['payer_email']);
|
||||
|
||||
// Express Checkout IPNs may not have the WPS email stored. But if it is,
|
||||
// make sure that the right account is being paid.
|
||||
$uc_paypal_wps_email = trim(variable_get('uc_paypal_wps_email', ''));
|
||||
if (!empty($uc_paypal_wps_email) && drupal_strtolower($receiver_email) != drupal_strtolower($uc_paypal_wps_email)) {
|
||||
watchdog('uc_paypal', 'IPN for a different PayPal account attempted.', array(), WATCHDOG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$req = '';
|
||||
|
||||
foreach ($_POST as $key => $value) {
|
||||
$value = urlencode(stripslashes($value));
|
||||
$req .= $key . '=' . $value . '&';
|
||||
}
|
||||
|
||||
$req .= 'cmd=_notify-validate';
|
||||
|
||||
if (variable_get('uc_paypal_wpp_server', '') == 'https://api-3t.paypal.com/nvp') {
|
||||
$host = 'https://www.paypal.com/cgi-bin/webscr';
|
||||
}
|
||||
else {
|
||||
$host = variable_get('uc_paypal_wps_server', 'https://www.sandbox.paypal.com/cgi-bin/webscr');
|
||||
}
|
||||
|
||||
$response = drupal_http_request($host, array(
|
||||
'method' => 'POST',
|
||||
'data' => $req,
|
||||
));
|
||||
|
||||
// TODO: Change this to property_exists when we have a PHP requirement >= 5.1.
|
||||
if (array_key_exists('error', $response)) {
|
||||
watchdog('uc_paypal', 'IPN failed with HTTP error @error, code @code.', array('@error' => $response->error, '@code' => $response->code), WATCHDOG_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp($response->data, 'VERIFIED') == 0) {
|
||||
watchdog('uc_paypal', 'IPN transaction verified.');
|
||||
|
||||
$duplicate = (bool) db_query_range('SELECT 1 FROM {uc_payment_paypal_ipn} WHERE txn_id = :id AND status <> :status', 0, 1, array(':id' => $txn_id, ':status' => 'Pending'))->fetchField();
|
||||
if ($duplicate) {
|
||||
if ($order->payment_method != 'credit') {
|
||||
watchdog('uc_paypal', 'IPN transaction ID has been processed before.', array(), WATCHDOG_NOTICE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
db_insert('uc_payment_paypal_ipn')
|
||||
->fields(array(
|
||||
'order_id' => $order_id,
|
||||
'txn_id' => $txn_id,
|
||||
'txn_type' => $txn_type,
|
||||
'mc_gross' => $payment_amount,
|
||||
'status' => $payment_status,
|
||||
'receiver_email' => $receiver_email,
|
||||
'payer_email' => $payer_email,
|
||||
'received' => REQUEST_TIME,
|
||||
))
|
||||
->execute();
|
||||
|
||||
switch ($payment_status) {
|
||||
case 'Canceled_Reversal':
|
||||
uc_order_comment_save($order_id, 0, t('PayPal has canceled the reversal and returned !amount !currency to your account.', array('!amount' => uc_currency_format($payment_amount, FALSE), '!currency' => $payment_currency)), 'admin');
|
||||
break;
|
||||
|
||||
case 'Completed':
|
||||
if (abs($payment_amount - $order->order_total) > 0.01) {
|
||||
watchdog('uc_paypal', 'Payment @txn_id for order @order_id did not equal the order total.', array('@txn_id' => $txn_id, '@order_id' => $order->order_id), WATCHDOG_WARNING, l(t('view'), 'admin/store/orders/' . $order->order_id));
|
||||
}
|
||||
$comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $txn_id));
|
||||
uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
|
||||
uc_cart_complete_sale($order);
|
||||
uc_order_comment_save($order_id, 0, t('PayPal IPN reported a payment of @amount @currency.', array('@amount' => uc_currency_format($payment_amount, FALSE), '@currency' => $payment_currency)));
|
||||
break;
|
||||
|
||||
case 'Denied':
|
||||
uc_order_comment_save($order_id, 0, t("You have denied the customer's payment."), 'admin');
|
||||
break;
|
||||
|
||||
case 'Expired':
|
||||
uc_order_comment_save($order_id, 0, t('The authorization has failed and cannot be captured.'), 'admin');
|
||||
break;
|
||||
|
||||
case 'Failed':
|
||||
uc_order_comment_save($order_id, 0, t("The customer's attempted payment from a bank account failed."), 'admin');
|
||||
break;
|
||||
|
||||
case 'Pending':
|
||||
uc_order_update_status($order_id, 'paypal_pending');
|
||||
uc_order_comment_save($order_id, 0, t('Payment is pending at PayPal: @reason', array('@reason' => _uc_paypal_pending_message(check_plain($_POST['pending_reason'])))), 'admin');
|
||||
break;
|
||||
|
||||
// You, the merchant, refunded the payment.
|
||||
case 'Refunded':
|
||||
$comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $txn_id));
|
||||
uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
|
||||
break;
|
||||
|
||||
case 'Reversed':
|
||||
watchdog('uc_paypal', 'PayPal has reversed a payment!', array(), WATCHDOG_ERROR);
|
||||
uc_order_comment_save($order_id, 0, t('Payment has been reversed by PayPal: @reason', array('@reason' => _uc_paypal_reversal_message(check_plain($_POST['reason_code'])))), 'admin');
|
||||
break;
|
||||
|
||||
case 'Processed':
|
||||
uc_order_comment_save($order_id, 0, t('A payment has been accepted.'), 'admin');
|
||||
break;
|
||||
|
||||
case 'Voided':
|
||||
uc_order_comment_save($order_id, 0, t('The authorization has been voided.'), 'admin');
|
||||
break;
|
||||
}
|
||||
}
|
||||
elseif (strcmp($response->data, 'INVALID') == 0) {
|
||||
watchdog('uc_paypal', 'IPN transaction failed verification.', array(), WATCHDOG_ERROR);
|
||||
uc_order_comment_save($order_id, 0, t('An IPN transaction failed verification for this order.'), 'admin');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the review page for Express Checkout Mark Flow.
|
||||
*/
|
||||
function uc_paypal_ec_review_redirect() {
|
||||
if (!isset($_SESSION['TOKEN']) || ($order = uc_order_load($_SESSION['cart_order'])) == FALSE) {
|
||||
unset($_SESSION['cart_order']);
|
||||
unset($_SESSION['have_details']);
|
||||
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
|
||||
drupal_set_message(t('An error has occurred in your PayPal payment. Please review your cart and try again.'));
|
||||
drupal_goto('cart');
|
||||
}
|
||||
|
||||
$nvp_request = array(
|
||||
'METHOD' => 'GetExpressCheckoutDetails',
|
||||
'TOKEN' => $_SESSION['TOKEN'],
|
||||
);
|
||||
|
||||
$nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
|
||||
|
||||
$_SESSION['PAYERID'] = $nvp_response['PAYERID'];
|
||||
|
||||
drupal_goto('cart/checkout/review');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the review page for Express Checkout Shortcut Flow.
|
||||
*/
|
||||
function uc_paypal_ec_review() {
|
||||
if (!isset($_SESSION['TOKEN']) || ($order = uc_order_load($_SESSION['cart_order'])) == FALSE) {
|
||||
unset($_SESSION['cart_order']);
|
||||
unset($_SESSION['have_details']);
|
||||
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
|
||||
drupal_set_message(t('An error has occurred in your PayPal payment. Please review your cart and try again.'));
|
||||
drupal_goto('cart');
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['have_details'][$order->order_id])) {
|
||||
$nvp_request = array(
|
||||
'METHOD' => 'GetExpressCheckoutDetails',
|
||||
'TOKEN' => $_SESSION['TOKEN'],
|
||||
);
|
||||
|
||||
$nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
|
||||
|
||||
$_SESSION['PAYERID'] = $nvp_response['PAYERID'];
|
||||
|
||||
$shipname = check_plain($nvp_response['SHIPTONAME']);
|
||||
if (strpos($shipname, ' ') > 0) {
|
||||
$order->delivery_first_name = substr($shipname, 0, strrpos(trim($shipname), ' '));
|
||||
$order->delivery_last_name = substr($shipname, strrpos(trim($shipname), ' ') + 1);
|
||||
}
|
||||
else {
|
||||
$order->delivery_first_name = $shipname;
|
||||
$order->delivery_last_name = '';
|
||||
}
|
||||
|
||||
$country_id = db_query("SELECT country_id FROM {uc_countries} WHERE country_iso_code_2 = :code", array(':code' => $nvp_response['SHIPTOCOUNTRYCODE']))->fetchField();
|
||||
$zone_id = 0;
|
||||
|
||||
if (!empty($country_id) && isset($nvp_response['SHIPTOSTATE'])) {
|
||||
$zone = $nvp_response['SHIPTOSTATE'];
|
||||
$zone_id = db_query("SELECT zone_id FROM {uc_zones} WHERE zone_country_id = :id AND (zone_code = :code OR zone_name = :name)", array(':id' => $country_id, ':code' => $zone, ':name' => $zone))->fetchField();
|
||||
}
|
||||
|
||||
$order->delivery_street1 = check_plain($nvp_response['SHIPTOSTREET']);
|
||||
$order->delivery_street2 = isset($nvp_response['SHIPTOSTREET2']) ? check_plain($nvp_response['SHIPTOSTREET2']) : '';
|
||||
$order->delivery_city = check_plain($nvp_response['SHIPTOCITY']);
|
||||
$order->delivery_zone = !empty($zone_id) ? $zone_id : 0;
|
||||
$order->delivery_postal_code = check_plain($nvp_response['SHIPTOZIP']);
|
||||
$order->delivery_country = !empty($country_id) ? $country_id : 840;
|
||||
|
||||
$order->billing_first_name = check_plain($nvp_response['FIRSTNAME']);
|
||||
$order->billing_last_name = check_plain($nvp_response['LASTNAME']);
|
||||
$order->billing_street1 = check_plain($nvp_response['EMAIL']);
|
||||
|
||||
if (empty($order->primary_email)) {
|
||||
$order->primary_email = $nvp_response['EMAIL'];
|
||||
}
|
||||
$order->payment_method = 'paypal_ec';
|
||||
|
||||
uc_order_save($order);
|
||||
|
||||
$_SESSION['have_details'][$order->order_id] = TRUE;
|
||||
}
|
||||
|
||||
$build['instructions'] = array('#markup' => t("Your order is almost complete! Please fill in the following details and click 'Continue checkout' to finalize the purchase."));
|
||||
|
||||
$build['form'] = drupal_get_form('uc_paypal_ec_review_form', $order);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the form for the custom Review Payment screen for Express Checkout.
|
||||
*/
|
||||
function uc_paypal_ec_review_form($form, &$form_state, $order) {
|
||||
if (module_exists('uc_quote') && variable_get('uc_paypal_ec_review_shipping', TRUE) && uc_order_is_shippable($order)) {
|
||||
uc_checkout_pane_quotes('prepare', $order, NULL);
|
||||
$order->line_items = uc_order_load_line_items($order);
|
||||
uc_order_save($order);
|
||||
|
||||
$result = uc_checkout_pane_quotes('view', $order, NULL);
|
||||
$form['panes']['quotes'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Shipping cost'),
|
||||
'#collapsible' => FALSE,
|
||||
);
|
||||
$form['panes']['quotes'] += $result['contents'];
|
||||
unset($form['panes']['quotes']['quote_button']);
|
||||
|
||||
$form['shippable'] = array('#type' => 'value', '#value' => 'true');
|
||||
}
|
||||
|
||||
if (variable_get('uc_paypal_ec_review_company', TRUE)) {
|
||||
$form['delivery_company'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => uc_get_field_name('company'),
|
||||
'#description' => uc_order_is_shippable($order) ? t('Leave blank if shipping to a residence.') : '',
|
||||
'#default_value' => $order->delivery_company,
|
||||
);
|
||||
}
|
||||
|
||||
if (variable_get('uc_paypal_ec_review_phone', TRUE)) {
|
||||
$form['delivery_phone'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Contact phone number'),
|
||||
'#default_value' => $order->delivery_phone,
|
||||
'#size' => 24,
|
||||
);
|
||||
}
|
||||
|
||||
if (variable_get('uc_paypal_ec_review_comment', TRUE)) {
|
||||
$form['order_comments'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Order comments'),
|
||||
'#description' => t('Special instructions or notes regarding your order.'),
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($form)) {
|
||||
drupal_goto('cart/echeckout/submit');
|
||||
}
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Continue checkout'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
function uc_paypal_ec_review_form_validate($form, &$form_state) {
|
||||
if (!empty($form_state['values']['shippable']) && empty($form_state['values']['quotes']['quote_option'])) {
|
||||
form_set_error('shipping', t('You must calculate and select a shipping option.'));
|
||||
}
|
||||
}
|
||||
|
||||
function uc_paypal_ec_review_form_submit($form, &$form_state) {
|
||||
$order = uc_order_load($_SESSION['cart_order']);
|
||||
|
||||
if (!empty($form_state['values']['shippable'])) {
|
||||
$quote_option = explode('---', $form_state['values']['quotes']['quote_option']);
|
||||
$order->quote['method'] = $quote_option[0];
|
||||
$order->quote['accessorials'] = $quote_option[1];
|
||||
$methods = uc_quote_methods();
|
||||
$method = $methods[$quote_option[0]];
|
||||
|
||||
$label = $method['quote']['accessorials'][$quote_option[1]];
|
||||
|
||||
$quote_option = $form_state['values']['quotes']['quote_option'];
|
||||
$order->quote['rate'] = $form_state['values']['quotes'][$quote_option]['rate'];
|
||||
|
||||
$result = db_query("SELECT line_item_id FROM {uc_order_line_items} WHERE order_id = :id AND type = :type", array(':id' => $order->order_id, ':type' => 'shipping'));
|
||||
if ($lid = $result->fetchField()) {
|
||||
uc_order_update_line_item($lid, $label, $order->quote['rate']);
|
||||
}
|
||||
else {
|
||||
uc_order_line_item_add($order->order_id, 'shipping', $label, $order->quote['rate']);
|
||||
}
|
||||
}
|
||||
|
||||
if (variable_get('uc_paypal_ec_review_company', TRUE)) {
|
||||
$order->delivery_company = $form_state['values']['delivery_company'];
|
||||
}
|
||||
|
||||
if (variable_get('uc_paypal_ec_review_phone', TRUE)) {
|
||||
$order->delivery_phone = $form_state['values']['delivery_phone'];
|
||||
}
|
||||
|
||||
if (variable_get('uc_paypal_ec_review_comment', TRUE)) {
|
||||
db_delete('uc_order_comments')
|
||||
->condition('order_id', $order->order_id)
|
||||
->execute();
|
||||
uc_order_comment_save($order->order_id, 0, $form_state['values']['order_comments'], 'order');
|
||||
}
|
||||
|
||||
uc_order_save($order);
|
||||
|
||||
$form_state['redirect'] = 'cart/echeckout/submit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Presents the final total to the user for checkout!
|
||||
*/
|
||||
function uc_paypal_ec_submit() {
|
||||
if (!isset($_SESSION['TOKEN']) || ($order = uc_order_load($_SESSION['cart_order'])) == FALSE) {
|
||||
unset($_SESSION['cart_order'], $_SESSION['have_details']);
|
||||
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
|
||||
drupal_set_message(t('An error has occurred in your PayPal payment. Please review your cart and try again.'));
|
||||
drupal_goto('cart');
|
||||
}
|
||||
|
||||
drupal_add_css(drupal_get_path('module', 'uc_cart') . '/uc_cart.css');
|
||||
|
||||
$build['review'] = array(
|
||||
'#theme' => 'uc_cart_review_table',
|
||||
'#items' => $order->products,
|
||||
'#show_subtotal' => FALSE,
|
||||
);
|
||||
|
||||
$build['line_items'] = uc_order_pane_line_items('customer', $order);
|
||||
|
||||
$build['instructions'] = array('#markup' => '<p>' . t("Your order is not complete until you click the 'Submit order' button below. Your PayPal account will be charged for the amount shown above once your order is placed. You will receive confirmation once your payment is complete.") . '</p>');
|
||||
|
||||
$build['submit_form'] = drupal_get_form('uc_paypal_ec_submit_form');
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits an order, calling the NVP API to send the order total to PayPal.
|
||||
*/
|
||||
function uc_paypal_ec_submit_form($form, &$form_state) {
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Submit order'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a complete Website Payments Standard sale.
|
||||
*/
|
||||
function uc_paypal_complete($order) {
|
||||
// If the order ID specified in the return URL is not the same as the one in
|
||||
// the user's session, we need to assume this is either a spoof or that the
|
||||
// user tried to adjust the order on this side while at PayPal. If it was a
|
||||
// legitimate checkout, the IPN will still come in from PayPal so the order
|
||||
// gets processed correctly. We'll leave an ambiguous message just in case.
|
||||
if (!isset($_SESSION['cart_order']) || intval($_SESSION['cart_order']) != $order->order_id) {
|
||||
drupal_set_message(t('Thank you for your order! PayPal will notify us once your payment has been processed.'));
|
||||
drupal_goto('cart');
|
||||
}
|
||||
|
||||
// Ensure the payment method is PayPal WPS.
|
||||
if ($order->payment_method != 'paypal_wps') {
|
||||
drupal_goto('cart');
|
||||
}
|
||||
|
||||
// This lets us know it's a legitimate access of the complete page.
|
||||
$_SESSION['uc_checkout'][$_SESSION['cart_order']]['do_complete'] = TRUE;
|
||||
drupal_goto('cart/checkout/complete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a canceled Website Payments Standard sale.
|
||||
*/
|
||||
function uc_paypal_cancel() {
|
||||
unset($_SESSION['cart_order']);
|
||||
|
||||
drupal_set_message(t('Your PayPal payment was canceled. Please feel free to continue shopping or contact us for assistance.'));
|
||||
|
||||
drupal_goto(variable_get('uc_paypal_wps_cancel_return_url', 'cart'));
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Flat rate shipping method administration menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Configures the store default product shipping rates.
|
||||
*
|
||||
* @see uc_flatrate_admin_method_edit_form_submit()
|
||||
* @see uc_flatrate_admin_method_edit_form_delete()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_flatrate_admin_method_edit_form($form, &$form_state, $mid = 0) {
|
||||
if ($mid && ($method = db_query("SELECT * FROM {uc_flatrate_methods} WHERE mid = :mid", array(':mid' => $mid))->fetchObject())) {
|
||||
$form['mid'] = array('#type' => 'value', '#value' => $mid);
|
||||
}
|
||||
else {
|
||||
$method = (object) array(
|
||||
'title' => '',
|
||||
'label' => '',
|
||||
'base_rate' => '',
|
||||
'product_rate' => '',
|
||||
);
|
||||
}
|
||||
|
||||
$form['title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Shipping method title'),
|
||||
'#description' => t('The name shown to administrators distinguish this method from other flatrate methods.'),
|
||||
'#default_value' => $method->title,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Line item label'),
|
||||
'#description' => t('The name shown to the customer when they choose a shipping method at checkout.'),
|
||||
'#default_value' => $method->label,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['base_rate'] = array(
|
||||
'#type' => 'uc_price',
|
||||
'#title' => t('Base price'),
|
||||
'#description' => t('The starting price for shipping costs.'),
|
||||
'#default_value' => $method->base_rate,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['product_rate'] = array(
|
||||
'#type' => 'uc_price',
|
||||
'#title' => t('Default product shipping rate'),
|
||||
'#description' => t('Additional shipping cost per product in cart.'),
|
||||
'#default_value' => $method->product_rate,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Submit'),
|
||||
);
|
||||
|
||||
if (isset($form['mid'])) {
|
||||
$form['actions']['delete'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Delete'),
|
||||
'#validate' => array(),
|
||||
'#submit' => array('uc_flatrate_admin_method_edit_form_delete'),
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to delete a flatrate method.
|
||||
*
|
||||
* @see uc_flatrate_admin_method_edit_form()
|
||||
*/
|
||||
function uc_flatrate_admin_method_edit_form_delete($form, &$form_state) {
|
||||
drupal_goto('admin/store/settings/quotes/flatrate/' . $form_state['values']['mid'] . '/delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_flatrate_admin_method_edit_form().
|
||||
*
|
||||
* @see uc_flatrate_admin_method_edit_form()
|
||||
*/
|
||||
function uc_flatrate_admin_method_edit_form_submit($form, &$form_state) {
|
||||
if (isset($form_state['values']['mid'])) {
|
||||
drupal_write_record('uc_flatrate_methods', $form_state['values'], 'mid');
|
||||
drupal_set_message(t('Flat rate shipping method was updated.'));
|
||||
$form_state['redirect'] = 'admin/store/settings/quotes/methods';
|
||||
}
|
||||
else {
|
||||
drupal_write_record('uc_flatrate_methods', $form_state['values']);
|
||||
|
||||
// Ensure Rules picks up the new condition.
|
||||
entity_flush_caches();
|
||||
|
||||
drupal_set_message(t('Created and enabled new flat rate shipping method.'));
|
||||
$form_state['redirect'] = 'admin/store/settings/quotes/manage/get_quote_from_flatrate_' . $form_state['values']['mid'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms deletion of a flat rate shipping method.
|
||||
*
|
||||
* @see uc_flatrate_admin_method_confirm_delete_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_flatrate_admin_method_confirm_delete($form, &$form_state, $mid) {
|
||||
$form['mid'] = array('#type' => 'value', '#value' => $mid);
|
||||
|
||||
return confirm_form($form, t('Do you want to delete this shipping method?'),
|
||||
'admin/store/settings/quotes/methods',
|
||||
t('This will remove the shipping method and the product-specific overrides (if applicable). This action can not be undone.'),
|
||||
t('Delete'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_flatrate_admin_method_confirm_delete().
|
||||
*
|
||||
* @see uc_flatrate_admin_method_confirm_delete()
|
||||
*/
|
||||
function uc_flatrate_admin_method_confirm_delete_submit($form, &$form_state) {
|
||||
$mid = $form_state['values']['mid'];
|
||||
|
||||
db_delete('uc_flatrate_methods')
|
||||
->condition('mid', $mid)
|
||||
->execute();
|
||||
db_delete('uc_flatrate_products')
|
||||
->condition('mid', $mid)
|
||||
->execute();
|
||||
|
||||
rules_config_delete(array('get_quote_from_flatrate_' . $mid));
|
||||
|
||||
drupal_set_message(t('Flat rate shipping method deleted.'));
|
||||
$form_state['redirect'] = 'admin/store/settings/quotes/methods';
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
name = Flat rate
|
||||
description = Assigns a flat shipping rate to an order, plus an optional amount per product.
|
||||
dependencies[] = uc_quote
|
||||
package = Ubercart - fulfillment
|
||||
core = 7.x
|
||||
|
||||
configure = admin/store/settings/quotes/methods
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_flatrate module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_flatrate_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_flatrate_products'] = array(
|
||||
'description' => 'Stores product information for quantity-based shipping quotes methods.',
|
||||
'fields' => array(
|
||||
'vid' => array(
|
||||
'description' => 'The {uc_products}.vid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'nid' => array(
|
||||
'description' => 'The {uc_products}.nid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'mid' => array(
|
||||
'description' => 'The {uc_flatrate_methods}.mid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'rate' => array(
|
||||
'description' => 'The rate multiplier, in the store default currency, per each of this product in the shipment.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('vid', 'mid'),
|
||||
'foreign keys' => array(
|
||||
'uc_products' => array(
|
||||
'table' => 'uc_products',
|
||||
'columns' => array(
|
||||
'nid' => 'nid',
|
||||
'vid' => 'vid',
|
||||
),
|
||||
),
|
||||
'uc_flatrate_methods' => array(
|
||||
'table' => 'uc_flatrate_methods',
|
||||
'columns' => array('mid' => 'mid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_flatrate_methods'] = array(
|
||||
'description' => 'Stores quantity-based shipping quotes method information.',
|
||||
'fields' => array(
|
||||
'mid' => array(
|
||||
'description' => 'Primary key: The shipping quote method ID.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'title' => array(
|
||||
'description' => 'The method title, displayed on administration pages.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'label' => array(
|
||||
'description' => 'The user-facing label of the shipping method.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'base_rate' => array(
|
||||
'description' => 'The amount of shipping cost before product quantity is applied.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => TRUE,
|
||||
'default' => 0.0,
|
||||
),
|
||||
'product_rate' => array(
|
||||
'description' => 'The default rate multiplier, in the store default currency, per product in the shipment.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => TRUE,
|
||||
'default' => 0.0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('mid'),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_flatrate_update_last_removed() {
|
||||
return 6003;
|
||||
}
|
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Shipping quote module that defines a flat shipping rate for each product.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function uc_flatrate_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['admin/store/settings/quotes/methods/flatrate/add'] = array(
|
||||
'title' => 'Add flat rate quote',
|
||||
'description' => 'Create a new flat rate shipping quote.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_flatrate_admin_method_edit_form'),
|
||||
'access arguments' => array('configure quotes'),
|
||||
'type' => MENU_LOCAL_ACTION,
|
||||
'file' => 'uc_flatrate.admin.inc',
|
||||
);
|
||||
$items['admin/store/settings/quotes/methods/flatrate/%'] = array(
|
||||
'title' => 'Edit flat rate method',
|
||||
'description' => 'Edit an existing flat rate shipping quote.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_flatrate_admin_method_edit_form', 6),
|
||||
'access arguments' => array('configure quotes'),
|
||||
'file' => 'uc_flatrate.admin.inc',
|
||||
);
|
||||
$items['admin/store/settings/quotes/flatrate/%/delete'] = array(
|
||||
'title' => 'Delete flat rate method',
|
||||
'description' => 'Delete a flat rate shipping quote.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_flatrate_admin_method_confirm_delete', 5),
|
||||
'access arguments' => array('configure quotes'),
|
||||
'file' => 'uc_flatrate.admin.inc',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*
|
||||
* Adds a form element for the shipping rate of a product.
|
||||
*/
|
||||
function uc_flatrate_form_alter(&$form, &$form_state, $form_id) {
|
||||
if (uc_product_is_product_form($form)) {
|
||||
$weight = variable_get('uc_quote_method_weight', array());
|
||||
$result = db_query("SELECT mid, title, product_rate FROM {uc_flatrate_methods}");
|
||||
foreach ($result as $method) {
|
||||
// Ensure default weight is set.
|
||||
$weight += array('flatrate_' . $method->mid => 0);
|
||||
|
||||
if (!isset($form['shipping']['flatrate'])) {
|
||||
$form['shipping']['flatrate'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Flat shipping rates'),
|
||||
'#description' => t("Overrides the default shipping rate per product for each flat rate shipping method. Leave field empty to use the method's default value."),
|
||||
'#tree' => TRUE,
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
'#weight' => 0,
|
||||
);
|
||||
}
|
||||
$form['shipping']['flatrate'][$method->mid] = array(
|
||||
'#type' => 'uc_price',
|
||||
'#title' => check_plain($method->title),
|
||||
'#default_value' => isset($form['#node']->flatrate[$method->mid]) ? $form['#node']->flatrate[$method->mid] : '',
|
||||
'#description' => t('Default rate: %price', array('%price' => uc_currency_format($method->product_rate))),
|
||||
'#weight' => $weight['flatrate_' . $method->mid],
|
||||
'#empty_zero' => FALSE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_insert().
|
||||
*/
|
||||
function uc_flatrate_node_insert($node) {
|
||||
uc_flatrate_node_update($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_update().
|
||||
*/
|
||||
function uc_flatrate_node_update($node) {
|
||||
if (uc_product_is_product($node->type)) {
|
||||
if (isset($node->flatrate) && is_array($node->flatrate)) {
|
||||
if (empty($node->revision)) {
|
||||
db_delete('uc_flatrate_products')
|
||||
->condition('vid', $node->vid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
$query = db_insert('uc_flatrate_products')
|
||||
->fields(array('vid', 'nid', 'mid', 'rate'));
|
||||
|
||||
foreach ($node->flatrate as $mid => $rate) {
|
||||
if (is_numeric($rate) && $rate >= 0) {
|
||||
$query->values(array(
|
||||
'vid' => $node->vid,
|
||||
'nid' => $node->nid,
|
||||
'mid' => $mid,
|
||||
'rate' => $rate,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_load().
|
||||
*/
|
||||
function uc_flatrate_node_load($nodes, $types) {
|
||||
$vids = array();
|
||||
$product_types = uc_product_types();
|
||||
|
||||
foreach ($nodes as &$node) {
|
||||
if (in_array($node->type, $product_types)) {
|
||||
$vids[$node->nid] = $node->vid;
|
||||
}
|
||||
}
|
||||
|
||||
if ($vids) {
|
||||
$result = db_query("SELECT nid, mid, rate FROM {uc_flatrate_products} WHERE vid IN (:vids)", array(':vids' => $vids));
|
||||
foreach ($result as $method) {
|
||||
$nodes[$method->nid]->flatrate[$method->mid] = $method->rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_delete().
|
||||
*/
|
||||
function uc_flatrate_node_delete($node) {
|
||||
db_delete('uc_flatrate_products')
|
||||
->condition('nid', $node->nid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_revision_delete().
|
||||
*/
|
||||
function uc_flatrate_node_revision_delete($node) {
|
||||
db_delete('uc_flatrate_products')
|
||||
->condition('vid', $node->vid)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_shipping_method().
|
||||
*/
|
||||
function uc_flatrate_uc_shipping_method() {
|
||||
$methods = array();
|
||||
|
||||
$result = db_query("SELECT mid, title, label, base_rate, product_rate FROM {uc_flatrate_methods}");
|
||||
foreach ($result as $method) {
|
||||
$methods['flatrate_' . $method->mid] = array(
|
||||
'id' => 'flatrate_' . $method->mid,
|
||||
'module' => 'uc_flatrate',
|
||||
'title' => $method->title,
|
||||
'description' => t('!base_rate + !product_rate per item', array('!base_rate' => uc_currency_format($method->base_rate), '!product_rate' => uc_currency_format($method->product_rate))),
|
||||
'operations' => array(
|
||||
'edit' => array(
|
||||
'title' => t('edit'),
|
||||
'href' => 'admin/store/settings/quotes/methods/flatrate/' . $method->mid,
|
||||
),
|
||||
'delete' => array(
|
||||
'title' => t('delete'),
|
||||
'href' => 'admin/store/settings/quotes/flatrate/' . $method->mid . '/delete',
|
||||
),
|
||||
),
|
||||
'quote' => array(
|
||||
'type' => 'order',
|
||||
'callback' => 'uc_flatrate_quote',
|
||||
'accessorials' => array(
|
||||
$method->label,
|
||||
),
|
||||
),
|
||||
'enabled' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard callback to return a shipping rate via the flat rate method.
|
||||
*
|
||||
* @param $products
|
||||
* The order's products.
|
||||
* @param $details
|
||||
* Other order details including a shipping address.
|
||||
* @param $method
|
||||
* The shipping method to use to create the quote.
|
||||
*
|
||||
* @return
|
||||
* An array containing the shipping quote for the order.
|
||||
*/
|
||||
function uc_flatrate_quote($products, $details, $method) {
|
||||
$method = explode('_', $method['id']);
|
||||
$mid = $method[1];
|
||||
|
||||
if ($method = db_query("SELECT * FROM {uc_flatrate_methods} WHERE mid = :mid", array(':mid' => $mid))->fetchObject()) {
|
||||
// Start at the base rate.
|
||||
$rate = $method->base_rate;
|
||||
|
||||
foreach ($products as $product) {
|
||||
if (!isset($product->flatrate[$mid])) {
|
||||
// Add the method's default product rate.
|
||||
$rate += $method->product_rate * $product->qty;
|
||||
}
|
||||
else {
|
||||
// Add the product-specific rate.
|
||||
$rate += $product->flatrate[$mid] * $product->qty;
|
||||
}
|
||||
}
|
||||
|
||||
$quotes[] = array(
|
||||
'rate' => $rate,
|
||||
'label' => check_plain($method->label),
|
||||
'option_label' => check_plain($method->label),
|
||||
);
|
||||
}
|
||||
|
||||
return $quotes;
|
||||
}
|
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Ubercart Shipping Quote Tests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SimpleTests for Ubercart Shipping Quotes.
|
||||
*/
|
||||
class UbercartQuoteTestCase extends UbercartTestHelper {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Shipping Quotes',
|
||||
'description' => 'Test shipping quotes.',
|
||||
'group' => 'Ubercart',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DrupalWebTestCase::setUp().
|
||||
*/
|
||||
function setUp() {
|
||||
$modules = array('rules_admin', 'uc_payment', 'uc_payment_pack', 'uc_quote', 'uc_flatrate');
|
||||
$permissions = array('administer rules', 'bypass rules access');
|
||||
parent::setUp($modules, $permissions);
|
||||
module_load_include('inc', 'uc_flatrate', 'uc_flatrate.admin');
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a flat rate shipping quote with optional conditions.
|
||||
*
|
||||
* @param $edit
|
||||
* Data to use to create shipping quote, same format as the values
|
||||
* submitted from the add flatrate method form.
|
||||
* @param $condition
|
||||
* If specified, a RulesAnd component defining the conditions to apply
|
||||
* for this method.
|
||||
*/
|
||||
function createQuote($edit = array(), $condition = FALSE) {
|
||||
$edit += array(
|
||||
'title' => $this->randomName(8),
|
||||
'label' => $this->randomName(8),
|
||||
'base_rate' => mt_rand(1, 10),
|
||||
'product_rate' => mt_rand(1, 10),
|
||||
);
|
||||
$form_state = array('values' => $edit);
|
||||
drupal_form_submit('uc_flatrate_admin_method_edit_form', $form_state);
|
||||
$method = db_query("SELECT * FROM {uc_flatrate_methods} WHERE mid = :mid", array(':mid' => $form_state['values']['mid']))->fetchObject();
|
||||
if ($condition) {
|
||||
$name = 'get_quote_from_flatrate_' . $method->mid;
|
||||
$condition['LABEL'] = $edit['label'] . ' conditions';
|
||||
$oldconfig = rules_config_load($name);
|
||||
$newconfig = rules_import(array($name => $condition));
|
||||
$newconfig->id = $oldconfig->id;
|
||||
unset($newconfig->is_new);
|
||||
$newconfig->status = ENTITY_CUSTOM;
|
||||
$newconfig->save();
|
||||
entity_flush_caches();
|
||||
}
|
||||
$this->assertTrue($method->base_rate == $edit['base_rate'], 'Flatrate quote was created successfully');
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates selection of a delivery country on the checkout page.
|
||||
*
|
||||
* @param $country
|
||||
* The text version of the country name to select, e.g. "Canada" or
|
||||
* "United States".
|
||||
*/
|
||||
protected function selectCountry($country = "Canada") {
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadHTML($this->content);
|
||||
$parent = $dom->getElementById('edit-panes-delivery-delivery-country');
|
||||
$options = $parent->getElementsByTagName('option');
|
||||
for ($i = 0; $i < $options->length; $i++) {
|
||||
if ($options->item($i)->textContent == $country) {
|
||||
$options->item($i)->setAttribute('selected', 'selected');
|
||||
}
|
||||
else {
|
||||
$options->item($i)->removeAttribute('selected');
|
||||
}
|
||||
}
|
||||
$this->drupalSetContent($dom->saveHTML());
|
||||
return $this->drupalPostAjax(NULL, array(), 'panes[delivery][delivery_country]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates selection of a quote on the checkout page.
|
||||
*
|
||||
* @param $n
|
||||
* The index of the quote to select.
|
||||
*/
|
||||
protected function selectQuote($n) {
|
||||
// Get the list of available quotes.
|
||||
$xpath = '//*[@name="panes[quotes][quotes][quote_option]"]';
|
||||
$elements = $this->xpath($xpath);
|
||||
$vals = array();
|
||||
foreach ($elements as $element) {
|
||||
$vals[(string) $element['id']] = (string) $element['value'];
|
||||
}
|
||||
|
||||
// Set the checked attribute of the chosen quote.
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadHTML($this->content);
|
||||
$i = 0;
|
||||
$selected = '';
|
||||
foreach ($vals as $id => $value) {
|
||||
if ($i == $n) {
|
||||
$dom->getElementById($id)->setAttribute('checked', 'checked');
|
||||
$selected = $value;
|
||||
}
|
||||
else {
|
||||
$dom->getElementById($id)->removeAttribute('checked');
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$this->drupalSetContent($dom->saveHTML());
|
||||
|
||||
// Post the selection via Ajax.
|
||||
$option = array('panes[quotes][quotes][quote_option]' => $selected);
|
||||
return $this->drupalPostAjax(NULL, array(), $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies shipping pane is hidden when there are no shippable items.
|
||||
*/
|
||||
public function testNoQuote() {
|
||||
$product = $this->createProduct(array('shippable' => FALSE));
|
||||
$quote = $this->createQuote();
|
||||
$this->drupalPost('node/' . $product->nid, array(), t('Add to cart'));
|
||||
$this->drupalPost('cart', array('items[0][qty]' => 1), t('Checkout'));
|
||||
$this->assertNoText('Calculate shipping cost', 'Shipping pane is not present with no shippable item.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic flatrate shipping quote functionality.
|
||||
*/
|
||||
public function testQuote() {
|
||||
// Create product and quotes.
|
||||
$product = $this->createProduct();
|
||||
$quote1 = $this->createQuote();
|
||||
$quote2 = $this->createQuote(array(), array(
|
||||
'LABEL' => 'quote_conditions',
|
||||
'PLUGIN' => 'and',
|
||||
'REQUIRES' => array('rules'),
|
||||
'USES VARIABLES' => array(
|
||||
'order' => array(
|
||||
'type' => 'uc_order',
|
||||
'label' => 'Order'
|
||||
),
|
||||
),
|
||||
'AND' => array( array(
|
||||
'data_is' => array(
|
||||
'data' => array('order:delivery-address:country'),
|
||||
'value' => '840',
|
||||
),
|
||||
)),
|
||||
));
|
||||
// Define strings to test for.
|
||||
$qty = mt_rand(2, 100);
|
||||
foreach (array($quote1, $quote2) as $quote) {
|
||||
$quote->amount = uc_currency_format($quote->base_rate + $quote->product_rate * $qty);
|
||||
$quote->option_text = $quote->label . ': ' . $quote->amount;
|
||||
$quote->total = uc_currency_format($product->sell_price * $qty + $quote->base_rate + $quote->product_rate * $qty);
|
||||
}
|
||||
|
||||
// Add product to cart, update qty, and go to checkout page.
|
||||
$this->drupalPost('node/' . $product->nid, array(), t('Add to cart'));
|
||||
$this->drupalPost('cart', array('items[0][qty]' => $qty), t('Checkout'));
|
||||
$this->assertText($quote1->option_text, 'The default quote option is available');
|
||||
$this->assertText($quote2->option_text, 'The second quote option is available');
|
||||
$this->assertText($quote1->total, 'Order total includes the default quote.');
|
||||
|
||||
// Select a different quote and ensure the total updates correctly. Currently, we have to do this
|
||||
// by examining the ajax return value directly (rather than the page contents) because drupalPostAjax() can
|
||||
// only handle replacements via the 'wrapper' property, and the ajax callback may use a command with a selector.
|
||||
$edit = array('panes[quotes][quotes][quote_option]' => 'flatrate_2---0');
|
||||
$result = $this->ucPostAjax(NULL, $edit, $edit);
|
||||
$this->assertText($quote2->total, 'The order total includes the selected quote.');
|
||||
|
||||
// Switch to a different country and ensure the ajax updates the page correctly.
|
||||
$edit['panes[delivery][delivery_country]'] = 124;
|
||||
$result = $this->ucPostAjax(NULL, $edit, 'panes[delivery][delivery_country]');
|
||||
$this->assertText($quote1->option_text, 'The default quote is still available after changing the country.');
|
||||
$this->assertNoText($quote2->option_text, 'The second quote is no longer available after changing the country.');
|
||||
$this->assertText($quote1->total, 'The total includes the default quote.');
|
||||
|
||||
// Proceed to review page and ensure the correct quote is present.
|
||||
$edit['panes[quotes][quotes][quote_option]'] = 'flatrate_1---0';
|
||||
$edit = $this->populateCheckoutForm($edit);
|
||||
$this->drupalPost(NULL, $edit, t('Review order'));
|
||||
$this->assertRaw(t('Your order is almost complete.'));
|
||||
$this->assertText($quote1->total, 'The total is correct on the order review page.');
|
||||
|
||||
// Submit the review.
|
||||
$this->drupalPost(NULL, array(), t('Submit order'));
|
||||
$order_id = db_query("SELECT order_id FROM {uc_orders} WHERE delivery_first_name = :name", array(':name' => $edit['panes[delivery][delivery_first_name]']))->fetchField();
|
||||
if ($order_id) {
|
||||
$order = uc_order_load($order_id);
|
||||
foreach ($order->line_items as $line) {
|
||||
if ($line['type'] == 'shipping') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Verify line item is correct.
|
||||
$this->assertEqual($line['type'], 'shipping', t('The shipping line item was saved to the order.'));
|
||||
$this->assertEqual($quote1->amount, uc_currency_format($line['amount']), t('Stored shipping line item has the correct amount.'));
|
||||
|
||||
// Verify order total is correct on order-view form.
|
||||
$this->drupalGet('admin/store/orders/' . $order_id);
|
||||
$this->assertText($quote1->total, 'The total is correct on the order admin view page.');
|
||||
|
||||
// Verify shipping line item is correct on order edit form.
|
||||
$this->drupalGet('admin/store/orders/' . $order_id . '/edit');
|
||||
$this->assertFieldByName('line_items[' . $line['line_item_id'] . '][title]', $quote1->label, t('Found the correct shipping line item title.'));
|
||||
$this->assertFieldByName('line_items[' . $line['line_item_id'] . '][amount]', substr($quote1->amount, 1), t('Found the correct shipping line item title.'));
|
||||
|
||||
// Verify that the "get quotes" button works as expected.
|
||||
$result = $this->ucPostAjax('admin/store/orders/' . $order_id . '/edit', array(), array('op' => t('Get shipping quotes')));
|
||||
$this->assertText($quote1->option_text, 'The default quote is available on the order-edit page.');
|
||||
$this->assertNoText($quote2->option_text, 'The second quote is not available on the order-edit page.');
|
||||
}
|
||||
else {
|
||||
$this->fail('No order was created.');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Shipping quotes administration menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default shipping settings.
|
||||
*
|
||||
* Sets the default shipping location of the store. Allows the user to
|
||||
* determine which quotin methods are enabled and which take precedence over
|
||||
* the others. Also sets the default quote and shipping types of all products
|
||||
* in the store. Individual products may be configured differently.
|
||||
*
|
||||
* @see uc_quote_admin_settings_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_quote_admin_settings($form, &$form_state) {
|
||||
$address = variable_get('uc_quote_store_default_address', new UcAddress());
|
||||
|
||||
$form['uc_quote_log_errors'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Log errors during checkout to watchdog'),
|
||||
'#default_value' => variable_get('uc_quote_log_errors', FALSE),
|
||||
);
|
||||
$form['uc_quote_display_debug'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Display debug information to administrators.'),
|
||||
'#default_value' => variable_get('uc_quote_display_debug', FALSE),
|
||||
);
|
||||
$form['uc_quote_require_quote'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Prevent the customer from completing an order if a shipping quote is not selected.'),
|
||||
'#default_value' => variable_get('uc_quote_require_quote', TRUE),
|
||||
);
|
||||
|
||||
$form['uc_quote_pane_description'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Shipping quote pane description'),
|
||||
'#tree' => TRUE,
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
$form['uc_quote_pane_description']['text'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message text'),
|
||||
'#default_value' => variable_get('uc_quote_pane_description', t('Shipping quotes are generated automatically when you enter your address and may be updated manually with the button below.')),
|
||||
);
|
||||
|
||||
$form['uc_quote_err_msg'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Shipping quote error message'),
|
||||
'#tree' => TRUE,
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
$form['uc_quote_err_msg']['text'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message text'),
|
||||
'#default_value' => variable_get('uc_quote_err_msg', t("There were problems getting a shipping quote. Please verify the delivery and product information and try again.\nIf this does not resolve the issue, please call in to complete your order.")),
|
||||
);
|
||||
|
||||
$form['default_address'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Default pickup address'),
|
||||
'#description' => t("When delivering products to customers, the original location of the product must be known in order to accurately quote the shipping cost and set up a delivery. This form provides the default location for all products in the store. If a product's individual pickup address is blank, Ubercart uses the store's default pickup address specified here."),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
);
|
||||
$form['default_address']['address'] = array(
|
||||
'#type' => 'uc_address',
|
||||
'#default_value' => isset($form_state['values']) ? $form_state['values'] : $address,
|
||||
'#required' => FALSE,
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_quote_admin_settings().
|
||||
*
|
||||
* @see uc_quote_admin_settings()
|
||||
*/
|
||||
function uc_quote_admin_settings_submit($form, &$form_state) {
|
||||
$address = new UcAddress();
|
||||
$address->first_name = $form_state['values']['first_name'];
|
||||
$address->last_name = $form_state['values']['last_name'];
|
||||
$address->company = $form_state['values']['company'];
|
||||
$address->phone = $form_state['values']['phone'];
|
||||
$address->street1 = $form_state['values']['street1'];
|
||||
$address->street2 = $form_state['values']['street2'];
|
||||
$address->city = $form_state['values']['city'];
|
||||
$address->zone = $form_state['values']['zone'];
|
||||
$address->postal_code = $form_state['values']['postal_code'];
|
||||
$address->country = $form_state['values']['country'];
|
||||
|
||||
variable_set('uc_quote_store_default_address', $address);
|
||||
variable_set('uc_quote_log_errors', $form_state['values']['uc_quote_log_errors']);
|
||||
variable_set('uc_quote_display_debug', $form_state['values']['uc_quote_display_debug']);
|
||||
variable_set('uc_quote_require_quote', $form_state['values']['uc_quote_require_quote']);
|
||||
variable_set('uc_quote_pane_description', $form_state['values']['uc_quote_pane_description']['text']);
|
||||
variable_set('uc_quote_err_msg', $form_state['values']['uc_quote_err_msg']['text']);
|
||||
|
||||
drupal_set_message(t('The configuration options have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings for the shipping quote methods.
|
||||
*
|
||||
* Enables and reorders shipping quote methods. Sets the default shipping type.
|
||||
*
|
||||
* @see uc_quote_method_settings_validate()
|
||||
* @see uc_quote_method_settings_submit()
|
||||
* @see theme_uc_quote_method_settings()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_quote_method_settings($form, &$form_state) {
|
||||
$form['methods'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
foreach (uc_quote_methods(TRUE) as $method) {
|
||||
if (isset($method['quote'])) {
|
||||
$id = $method['id'];
|
||||
|
||||
// Build a list of operations links.
|
||||
$operations = isset($method['operations']) ? $method['operations'] : array();
|
||||
$operations += array('conditions' => array(
|
||||
'title' => t('conditions'),
|
||||
'href' => 'admin/store/settings/quotes/manage/get_quote_from_' . $id,
|
||||
'weight' => 5,
|
||||
));
|
||||
|
||||
// Ensure "delete" comes towards the end of the list.
|
||||
if (isset($operations['delete'])) {
|
||||
$operations['delete']['weight'] = 10;
|
||||
}
|
||||
uasort($operations, 'drupal_sort_weight');
|
||||
|
||||
$form['methods'][$id]['uc_quote_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => check_plain($method['title']),
|
||||
'#default_value' => $method['enabled'],
|
||||
);
|
||||
$form['methods'][$id]['description'] = array(
|
||||
'#markup' => isset($method['description']) ? $method['description'] : '',
|
||||
);
|
||||
$form['methods'][$id]['uc_quote_method_weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#default_value' => $method['weight'],
|
||||
'#attributes' => array('class' => array('uc-quote-method-weight')),
|
||||
);
|
||||
$form['methods'][$id]['operations'] = array(
|
||||
'#theme' => 'links',
|
||||
'#links' => $operations,
|
||||
'#attributes' => array('class' => array('links', 'inline')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$shipping_types = uc_quote_shipping_type_options();
|
||||
if (is_array($shipping_types)) {
|
||||
$form['uc_quote_type_weight'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('List position'),
|
||||
'#description' => t('Determines which shipping methods are quoted at checkout when products of different shipping types are ordered. Larger values take precedence.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$weight = variable_get('uc_quote_type_weight', array());
|
||||
$shipping_methods = module_invoke_all('uc_shipping_method');
|
||||
$method_types = array();
|
||||
foreach ($shipping_methods as $method) {
|
||||
// Get shipping method types from shipping methods that provide quotes
|
||||
if (isset($method['quote'])) {
|
||||
$method_types[$method['quote']['type']][] = $method['title'];
|
||||
}
|
||||
}
|
||||
if (isset($method_types['order']) && is_array($method_types['order'])) {
|
||||
$count = count($method_types['order']);
|
||||
$form['uc_quote_type_weight']['#description'] .= format_plural($count, '<br />The %list method is compatible with any shipping type.', '<br />The %list methods are compatible with any shipping type.', array('%list' => implode(', ', $method_types['order'])));
|
||||
}
|
||||
foreach ($shipping_types as $id => $title) {
|
||||
$form['uc_quote_type_weight'][$id] = array(
|
||||
'#type' => 'weight',
|
||||
'#title' => $title . (isset($method_types[$id]) && is_array($method_types[$id]) ? ' (' . implode(', ', $method_types[$id]) . ')' : ''),
|
||||
'#delta' => 5,
|
||||
'#default_value' => isset($weight[$id]) ? $weight[$id] : 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
$form['uc_store_shipping_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default order fulfillment type for products'),
|
||||
'#options' => $shipping_types,
|
||||
'#default_value' => variable_get('uc_store_shipping_type', 'small_package'),
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a formatted list of shipping quote methods and form elements.
|
||||
*
|
||||
* @see uc_quote_method_settings()
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_uc_quote_method_settings($variables) {
|
||||
$form = $variables['form'];
|
||||
|
||||
drupal_add_tabledrag('uc-quote-methods', 'order', 'sibling', 'uc-quote-method-weight');
|
||||
|
||||
$header = array(t('Shipping method'), t('Details'), array('data' => t('List position'), 'sort' => 'asc'), t('Operations'));
|
||||
$rows = array();
|
||||
foreach (element_children($form['methods']) as $method) {
|
||||
$row = array(
|
||||
drupal_render($form['methods'][$method]['uc_quote_enabled']),
|
||||
drupal_render($form['methods'][$method]['description']),
|
||||
drupal_render($form['methods'][$method]['uc_quote_method_weight']),
|
||||
drupal_render($form['methods'][$method]['operations']),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
'data' => $row,
|
||||
'class' => array('draggable'),
|
||||
);
|
||||
}
|
||||
|
||||
$output = theme('table', array(
|
||||
'header' => $header,
|
||||
'rows' => $rows,
|
||||
'attributes' => array('id' => 'uc-quote-methods'),
|
||||
'empty' => t('No shipping quotes have been configured yet.'),
|
||||
));
|
||||
$output .= drupal_render_children($form);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validation for uc_quote_method_settings().
|
||||
*
|
||||
* Requires at least one enabled shipping method.
|
||||
*
|
||||
* @see uc_quote_method_settings()
|
||||
* @see uc_quote_method_settings_submit()
|
||||
*/
|
||||
function uc_quote_method_settings_validate($form, &$form_state) {
|
||||
$none_enabled = TRUE;
|
||||
if (is_array($form_state['values']['methods'])) {
|
||||
foreach ($form_state['values']['methods'] as $method) {
|
||||
if ($method['uc_quote_enabled']) {
|
||||
$none_enabled = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($none_enabled) {
|
||||
form_set_error('uc_quote_enabled', t('At least one shipping quote method must be enabled.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_quote_method_settings().
|
||||
*
|
||||
* @see uc_quote_method_settings()
|
||||
* @see uc_quote_method_settings_validate()
|
||||
*/
|
||||
function uc_quote_method_settings_submit($form, &$form_state) {
|
||||
$enabled = array();
|
||||
$method_weight = array();
|
||||
foreach ($form_state['values']['methods'] as $id => $method) {
|
||||
$enabled[$id] = $method['uc_quote_enabled'];
|
||||
$method_weight[$id] = $method['uc_quote_method_weight'];
|
||||
}
|
||||
|
||||
variable_set('uc_quote_enabled', $enabled);
|
||||
variable_set('uc_quote_method_weight', $method_weight);
|
||||
variable_set('uc_quote_type_weight', $form_state['values']['uc_quote_type_weight']);
|
||||
variable_set('uc_store_shipping_type', $form_state['values']['uc_store_shipping_type']);
|
||||
drupal_set_message(t('The configuration options have been saved.'));
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Shipping Quotes module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines callbacks and service options for shipping methods.
|
||||
*
|
||||
* @return
|
||||
* An array of shipping methods, keyed by the unique method ID, and with the
|
||||
* following members:
|
||||
* - id: The unique method ID, the same as the array key for this method.
|
||||
* - module: The name of the implementing module.
|
||||
* - title: The shipping method title.
|
||||
* - description: (optional) A short description of the shipping method.
|
||||
* - operations: (optional) A set of links that can be used to edit or
|
||||
* configure the shipping method, suitable for passing to theme_links().
|
||||
* - quote: (optional) An associative array, with the following members:
|
||||
* - type: The quote and shipping types are ids of the product shipping type
|
||||
* that these methods apply to. type may also be 'order' which indicates
|
||||
* that the quote applies to the entire order, regardless of the shipping
|
||||
* types of its products. This is used by quote methods that are based on
|
||||
* the location of the customer rather than their purchase.
|
||||
* - callback: The function that is called by uc_quote when a shipping quote
|
||||
* is requested. Its arguments are the array of products and an array of
|
||||
* order details (the shipping address). The return value is an array
|
||||
* representing the rates quoted and errors returned (if any) for each
|
||||
* option in the accessorials array.
|
||||
* - file: (optional) The name of the file that contains the callback
|
||||
* function.
|
||||
* - accessorials: This array represents the different options the customer
|
||||
* may choose for their shipment. The callback function should generate a
|
||||
* quote for each option in accessorials and return them via an array.
|
||||
* @code
|
||||
* return array(
|
||||
* '03' => array('rate' => 15.75, 'option_label' => t('UPS Ground'),
|
||||
* 'error' => 'Additional handling charge applied.'),
|
||||
* '14' => array('error' => 'Invalid package type.'),
|
||||
* '59' => array('rate' => 26.03, 'option_label' => t('UPS 2nd Day'))
|
||||
* );
|
||||
* @endcode
|
||||
* - pkg_types: The list of package types that the shipping method can
|
||||
* handle. This should be an associative array that can be used as the
|
||||
* #options of a select form element. It is recommended that a function
|
||||
* be written to output this array so the method doesn't need to be found
|
||||
* just for the package types.
|
||||
* - ship: (optional) An associative array, in the same format as 'quote'.
|
||||
* - enabled: (optional) Whether the method should be enabled by default.
|
||||
* Defaults to FALSE.
|
||||
* - weight: (optional) The default position of the method in the list of
|
||||
* shipping quotes. Defaults to 0.
|
||||
*/
|
||||
function hook_uc_shipping_method() {
|
||||
$methods = array();
|
||||
|
||||
$methods['ups'] = array(
|
||||
'id' => 'ups',
|
||||
'title' => t('UPS'),
|
||||
'quote' => array(
|
||||
'type' => 'small package',
|
||||
'callback' => 'uc_ups_quote',
|
||||
'accessorials' => array(
|
||||
'03' => t('UPS Ground'),
|
||||
'11' => t('UPS Standard'),
|
||||
'01' => t('UPS Next Day Air'),
|
||||
'13' => t('UPS Next Day Air Saver'),
|
||||
'14' => t('UPS Next Day Early A.M.'),
|
||||
'02' => t('UPS 2nd Day Air'),
|
||||
'59' => t('UPS 2nd Day Air A.M.'),
|
||||
'12' => t('UPS 3-Day Select'),
|
||||
),
|
||||
),
|
||||
'ship' => array(
|
||||
'type' => 'small package',
|
||||
'callback' => 'uc_ups_fulfill_order',
|
||||
'pkg_types' => array(
|
||||
'01' => t('UPS Letter'),
|
||||
'02' => t('Customer Supplied Package'),
|
||||
'03' => t('Tube'),
|
||||
'04' => t('PAK'),
|
||||
'21' => t('UPS Express Box'),
|
||||
'24' => t('UPS 25KG Box'),
|
||||
'25' => t('UPS 10KG Box'),
|
||||
'30' => t('Pallet'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines shipping types for shipping methods.
|
||||
*
|
||||
* This hook defines a shipping type that this module is designed to handle.
|
||||
* These types are specified by a machine- and human-readable name called 'id',
|
||||
* and 'title' respectively. Shipping types may be set for individual products,
|
||||
* manufacturers, and for the entire store catalog. Shipping modules should be
|
||||
* careful to use the same shipping type ids as other similar shipping modules
|
||||
* (i.e., FedEx and UPS both operate on "small package" shipments). Modules that
|
||||
* do not fulfill orders may not need to implement this hook.
|
||||
*
|
||||
* @return
|
||||
* An array of shipping types keyed by a machine-readable name.
|
||||
*/
|
||||
function hook_uc_shipping_type() {
|
||||
$weight = variable_get('uc_quote_type_weight', array('small_package' => 0));
|
||||
|
||||
$types = array();
|
||||
$types['small_package'] = array(
|
||||
'id' => 'small_package',
|
||||
'title' => t('Small package'),
|
||||
'weight' => $weight['small_package'],
|
||||
);
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file
|
||||
* Styles for shipping quote cart and checkout panes.
|
||||
*/
|
||||
|
||||
#uc-cart-pane-quotes {
|
||||
border: solid 1px #bbb;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#uc-cart-pane-quotes .quote-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#uc-cart-pane-quotes .uc-store-address-field .form-item label {
|
||||
padding: 5px 6px 6px;
|
||||
}
|
||||
|
||||
#uc-cart-pane-quotes .form-submit {
|
||||
margin-left: 16em;
|
||||
}
|
||||
|
||||
#quote {
|
||||
border: solid 1px #bbb;
|
||||
margin-top: 1em;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#uc-paypal-ec-review-form #quote {
|
||||
border: 0;
|
||||
padding: 0.5em 0 0;
|
||||
}
|
||||
|
||||
.quote-error {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.quote-notes {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles for uc_ups.
|
||||
*/
|
||||
.ups-logo {
|
||||
vertical-align: middle;
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
name = Shipping quotes
|
||||
description = Enables shipping quotes to be calculated and displayed at checkout.
|
||||
dependencies[] = uc_cart
|
||||
package = Ubercart - core (optional)
|
||||
core = 7.x
|
||||
|
||||
configure = admin/store/settings/quotes
|
||||
|
||||
; Test cases
|
||||
files[] = tests/uc_quote.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity metadata hooks for uc_quote.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter().
|
||||
*/
|
||||
function uc_quote_entity_property_info_alter(&$info) {
|
||||
$info['uc_order_product']['properties']['shipping_type'] = array(
|
||||
'type' => 'text',
|
||||
'label' => t('Shipping type'),
|
||||
'getter callback' => 'uc_product_get_shipping_type', // not a typical callback.
|
||||
);
|
||||
}
|
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_quote module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_quote_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_quote_shipping_types'] = array(
|
||||
'description' => 'Stores shipping information of products.',
|
||||
'fields' => array(
|
||||
'id_type' => array(
|
||||
'description' => 'Determines the table that id references. "product" => {uc_products}.nid.',
|
||||
'type' => 'varchar',
|
||||
'length' => 127,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'id' => array(
|
||||
'description' => 'The entity ID.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'shipping_type' => array(
|
||||
'description' => 'The basic type of shipment, e.g.: small package, freight.',
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
),
|
||||
'primary key' => array('id_type', 'id'),
|
||||
);
|
||||
|
||||
$schema['uc_quote_product_locations'] = array(
|
||||
'description' => 'Stores default product origin addresses.',
|
||||
'fields' => array(
|
||||
'nid' => array(
|
||||
'description' => 'The {uc_products}.nid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'first_name' => array(
|
||||
'description' => 'The address first name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'last_name' => array(
|
||||
'description' => 'The address last name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'company' => array(
|
||||
'description' => 'The address company.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'street1' => array(
|
||||
'description' => 'The address street line 1.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'street2' => array(
|
||||
'description' => 'The address street line 2.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'city' => array(
|
||||
'description' => 'The address city.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'zone' => array(
|
||||
'description' => 'The address state/province, from {uc_zones}.zone_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'postal_code' => array(
|
||||
'description' => 'The address postal code.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'country' => array(
|
||||
'description' => 'The address country, from {uc_countries}.country_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'phone' => array(
|
||||
'description' => 'The address phone number.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
),
|
||||
'primary key' => array('nid'),
|
||||
'foreign keys' => array(
|
||||
'uc_products' => array(
|
||||
'table' => 'uc_products',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_order_quotes'] = array(
|
||||
'description' => 'Stores shipping quotes.',
|
||||
'fields' => array(
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'method' => array(
|
||||
'description' => 'The quoted shipping method.',
|
||||
'type' => 'varchar',
|
||||
'length' => 25,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'accessorials' => array(
|
||||
'description' => 'Additional services or special instructions.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'rate' => array(
|
||||
'description' => 'The quoted shipping rate.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'unique keys' => array(
|
||||
'order_id_quote_method' => array('order_id', 'method'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_quote_uninstall() {
|
||||
db_delete('variable')
|
||||
->condition('name', 'uc_quote_%', 'LIKE')
|
||||
->execute();
|
||||
|
||||
variable_del('uc_store_shipping_type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_quote_update_last_removed() {
|
||||
return 6004;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops {uc_order_quotes}.quote_form.
|
||||
*/
|
||||
function uc_quote_update_7001() {
|
||||
db_drop_field('uc_order_quotes', 'quote_form');
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Menu callbacks for shipping quotes requested through AJAX.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pulls the get_quote_from_* triggers and assembles their returned data.
|
||||
*/
|
||||
function uc_quote_assemble_quotes($order) {
|
||||
$products = $order->products;
|
||||
foreach ($products as $id => $product) {
|
||||
$node = (array) node_load($product->nid);
|
||||
foreach ($node as $key => $value) {
|
||||
if (!isset($product->$key)) {
|
||||
$product->$key = $value;
|
||||
}
|
||||
}
|
||||
$order->products[$id] = $product;
|
||||
}
|
||||
|
||||
$shipping_types = array();
|
||||
foreach ($products as $product) {
|
||||
$shipping_types[] = uc_product_get_shipping_type($product);
|
||||
}
|
||||
|
||||
$shipping_types = array_unique($shipping_types);
|
||||
$all_types = uc_quote_get_shipping_types();
|
||||
$shipping_type = '';
|
||||
|
||||
// Use the most prominent shipping type (highest weight).
|
||||
// In theory, you can ship lighter products with heavier by the same
|
||||
// method, but not vice versa.
|
||||
$type_weight = -1000; // arbitrary low number
|
||||
foreach ($shipping_types as $type) {
|
||||
if ($all_types[$type]['weight'] > $type_weight) {
|
||||
$shipping_type = $all_types[$type]['id'];
|
||||
$type_weight = $all_types[$type]['weight'];
|
||||
}
|
||||
}
|
||||
$methods = uc_quote_methods();
|
||||
foreach ($methods as $id => $method) {
|
||||
if (!isset($method['quote']) || ($method['quote']['type'] != 'order' && $method['quote']['type'] != $shipping_type)) {
|
||||
unset($methods[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
$quote_data = array();
|
||||
foreach ($methods as $method) {
|
||||
$set = rules_config_load('get_quote_from_' . $method['id']);
|
||||
if (!$set || $set->execute($order)) {
|
||||
$data = uc_quote_action_get_quote($order, $method);
|
||||
|
||||
foreach ($data as &$quote) {
|
||||
if (isset($quote['rate'])) {
|
||||
$quote['format'] = uc_currency_format($quote['rate']);
|
||||
}
|
||||
}
|
||||
$quote_data[$method['id']] = $data;
|
||||
}
|
||||
}
|
||||
return $quote_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves shipping quote.
|
||||
*
|
||||
* @param $order
|
||||
* The order the quote is for.
|
||||
* @param $method
|
||||
* The shipping method to generate the quote.
|
||||
*
|
||||
* @return
|
||||
* Array of shipping quotes.
|
||||
*/
|
||||
function uc_quote_action_get_quote($order, $method) {
|
||||
$details = array();
|
||||
foreach ($order as $key => $value) {
|
||||
if (substr($key, 0, 9) == 'delivery_') {
|
||||
$field = substr($key, 9);
|
||||
$details[$field] = $value;
|
||||
}
|
||||
}
|
||||
ob_start();
|
||||
// Load include file containing quote callback, if there is one
|
||||
if (isset($method['quote']['file'])) {
|
||||
$inc_file = drupal_get_path('module', $method['module']) . '/' . $method['quote']['file'];
|
||||
if (is_file($inc_file)) {
|
||||
require_once($inc_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (function_exists($method['quote']['callback'])) {
|
||||
// This feels wrong, but it's the only way I can ensure that shipping
|
||||
// methods won't mess up the products in their methods.
|
||||
$products = array();
|
||||
foreach ($order->products as $key => $item) {
|
||||
if (uc_order_product_is_shippable($item)) {
|
||||
$products[$key] = clone $item;
|
||||
}
|
||||
}
|
||||
$quote_data = call_user_func($method['quote']['callback'], $products, $details, $method);
|
||||
}
|
||||
$messages = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if ($messages && variable_get('uc_quote_log_errors', FALSE)) {
|
||||
watchdog('quote', '!messages', array('!messages' => $messages), WATCHDOG_WARNING);
|
||||
watchdog('quote', '<pre>@data</pre>', array('@data' => print_r($quote_data, TRUE)), WATCHDOG_WARNING);
|
||||
}
|
||||
return $quote_data;
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules hooks for uc_quote.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_rules_condition_info().
|
||||
*/
|
||||
function uc_quote_rules_condition_info() {
|
||||
return array(
|
||||
'uc_quote_condition_order_shipping_method' => array(
|
||||
'label' => t("Order has a shipping quote from a particular method"),
|
||||
'group' => t('Order: Shipping Quote'),
|
||||
'base' => 'uc_quote_condition_order_shipping_method',
|
||||
'parameter' => array(
|
||||
'order' => array('type' => 'uc_order', 'label' => t('Order')),
|
||||
'method' => array('type' => 'text', 'label' => t('Shipping method'), 'options list' => 'uc_quote_condition_order_shipping_method_options'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks an order's shipping method.
|
||||
*/
|
||||
function uc_quote_condition_order_shipping_method($order, $method) {
|
||||
// Check the easy way first.
|
||||
if (!empty($order->quote)) {
|
||||
return $order->quote['method'] == $method;
|
||||
}
|
||||
// Otherwise, look harder.
|
||||
if (!empty($order->line_items)) {
|
||||
$methods = module_invoke_all('uc_shipping_method');
|
||||
$accessorials = $methods[$method]['quote']['accessorials'];
|
||||
|
||||
foreach ($order->line_items as $line_item) {
|
||||
if ($line_item['type'] == 'shipping' && in_array($line_item['title'], $accessorials)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options callback.
|
||||
*
|
||||
* @see uc_quote_condition_order_shipping_method()
|
||||
*/
|
||||
function uc_quote_condition_order_shipping_method_options() {
|
||||
$methods = module_invoke_all('uc_shipping_method');
|
||||
$enabled = variable_get('uc_quote_enabled', array());
|
||||
|
||||
$options = array();
|
||||
foreach ($methods as $id => $method) {
|
||||
$options[$id] = $method['title'];
|
||||
if (!isset($enabled[$id]) || !$enabled[$id]) {
|
||||
$options[$id] .= ' ' . t('(disabled)');
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules configurations for shipping quotes modules.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_default_rules_configuration().
|
||||
*/
|
||||
function uc_quote_default_rules_configuration() {
|
||||
$configs = array();
|
||||
$methods = module_invoke_all('uc_shipping_method');
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$set = rules_and(array(
|
||||
'order' => array('type' => 'uc_order', 'label' => t('Order')),
|
||||
));
|
||||
$set->label = t('@method conditions', array('@method' => $method['title']));
|
||||
|
||||
$configs['get_quote_from_' . $method['id']] = $set;
|
||||
}
|
||||
|
||||
return $configs;
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Theme functions for the uc_quote module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Displays the formatted quote cart pane.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - form: A render element representing the form.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_uc_cart_pane_quotes($variables) {
|
||||
$form = $variables['form'];
|
||||
|
||||
$output = '<p class="quote-title">' . t('Estimated shipping cost:') . '</p>';
|
||||
$output .= drupal_render_children($form);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the returned shipping rates.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - form: A render element representing the form.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_uc_quote_returned_rates($variables) {
|
||||
$form = $variables['form'];
|
||||
$output = '';
|
||||
|
||||
$keys = element_children($form);
|
||||
|
||||
// Render notes and error messages after each radio button.
|
||||
if (count($keys) > 1) {
|
||||
foreach ($keys as $key) {
|
||||
if ($key == 'quote_option') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($form['quote_option'][$key])) {
|
||||
$output .= drupal_render($form['quote_option'][$key]);
|
||||
}
|
||||
$output .= drupal_render($form[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$output .= drupal_render_children($form);
|
||||
|
||||
return $output;
|
||||
}
|
After Width: | Height: | Size: 616 B |
After Width: | Height: | Size: 365 B |
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation to display a printable Ubercart packing slip.
|
||||
*
|
||||
* @see template_preprocess_uc_order_invoice_page()
|
||||
*/
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>">
|
||||
|
||||
<head>
|
||||
<title><?php print t('Packing slip'); ?></title>
|
||||
<style>
|
||||
.page-break {
|
||||
page-break-before: always;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php print $content; ?>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* The shipment packing slip template.
|
||||
*/
|
||||
?>
|
||||
|
||||
<table width="95%" border="0" cellspacing="0" cellpadding="1" align="center" bgcolor="#006699" style="font-family: verdana, arial, helvetica; font-size: small;">
|
||||
<tr>
|
||||
<td>
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="5" align="center" bgcolor="#FFFFFF" style="font-family: verdana, arial, helvetica; font-size: small;">
|
||||
<tr valign="top">
|
||||
<td>
|
||||
<table width="100%" style="font-family: verdana, arial, helvetica; font-size: small;">
|
||||
<tr>
|
||||
<td>
|
||||
<?php print $site_logo; ?>
|
||||
</td>
|
||||
<td width="20%" nowrap="nowrap">
|
||||
<?php print $store_address; ?>
|
||||
<br />
|
||||
<?php print $store_phone; ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td>
|
||||
|
||||
<table cellpadding="4" cellspacing="0" border="0" width="100%" style="font-family: verdana, arial, helvetica; font-size: small;">
|
||||
<tr>
|
||||
<td colspan="2" bgcolor="#006699" style="color: white;">
|
||||
<b><?php echo t('Purchasing Information:'); ?></b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td nowrap="nowrap">
|
||||
<b><?php echo t('E-mail Address:'); ?></b>
|
||||
</td>
|
||||
<td width="98%">
|
||||
<?php print $order_email; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
|
||||
<table width="100%" cellspacing="0" cellpadding="0" style="font-family: verdana, arial, helvetica; font-size: small;">
|
||||
<tr>
|
||||
<td valign="top" width="50%">
|
||||
<b><?php echo t('Billing Address:'); ?></b><br />
|
||||
<?php print $billing_address; ?><br />
|
||||
<br />
|
||||
<b><?php echo t('Billing Phone:'); ?></b><br />
|
||||
<?php print $billing_phone; ?><br />
|
||||
</td>
|
||||
<td valign="top" width="50%">
|
||||
<b><?php echo t('Shipping Address:'); ?></b><br />
|
||||
<?php print $shipping_address; ?><br />
|
||||
<br />
|
||||
<b><?php echo t('Shipping Phone:'); ?></b><br />
|
||||
<?php print $shipping_phone; ?><br />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td nowrap="nowrap">
|
||||
<b><?php echo t('Payment Method:'); ?></b>
|
||||
</td>
|
||||
<td width="98%">
|
||||
<?php print $payment_method; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2" bgcolor="#006699" style="color: white;">
|
||||
<b><?php echo t('Order Summary:'); ?></b>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2" bgcolor="#EEEEEE">
|
||||
<font color="#CC6600"><b><?php echo t('Shipping Details:'); ?></b></font>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
|
||||
<table border="0" cellpadding="1" cellspacing="0" width="100%" style="font-family: verdana, arial, helvetica; font-size: small;">
|
||||
<tr>
|
||||
<td nowrap="nowrap">
|
||||
<b><?php echo t('Order #:'); ?></b>
|
||||
<?php print $order_link; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td nowrap="nowrap">
|
||||
<b><?php echo t('Carrier:'); ?></b>
|
||||
<?php print $carrier; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td nowrap="nowrap">
|
||||
<b><?php echo t('Tracking #:'); ?></b>
|
||||
<?php print $tracking_number; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<br /><br /><b><?php echo t('Products on order:'); ?> </b>
|
||||
|
||||
<table width="100%" style="font-family: verdana, arial, helvetica; font-size: small;">
|
||||
|
||||
<?php if (is_array($packages)) {
|
||||
foreach ($packages as $package) {
|
||||
foreach ($package->products as $product) { ?>
|
||||
<tr>
|
||||
<td valign="top" nowrap="nowrap">
|
||||
<b><?php print $product->qty; ?> x </b>
|
||||
</td>
|
||||
<td width="98%">
|
||||
<b><?php print $product->title; ?></b>
|
||||
<br />
|
||||
<?php echo t('SKU: ') . $product->model; ?><br />
|
||||
<?php if (isset($product->data['attributes']) && is_array($product->data['attributes']) && count($product->data['attributes']) > 0) {
|
||||
foreach ($product->data['attributes'] as $attribute => $option) {
|
||||
echo '<li>' . t('@attribute: @options', array('@attribute' => $attribute, '@options' => implode(', ', (array)$option))) . '</li>';
|
||||
}
|
||||
} ?>
|
||||
<br />
|
||||
</td>
|
||||
</tr>
|
||||
<?php }
|
||||
}
|
||||
} ?>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Shipping module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles additional data and activity for shipments.
|
||||
*
|
||||
* Adds additional activity after shipment objects are loaded from,
|
||||
* saved to, or deleted from the database. This is useful for shipment
|
||||
* modules that store method-specific shipment data in separate tables that
|
||||
* need to be kept in sync with the uc_shipments table.
|
||||
*
|
||||
* The members of the shipment object are the fields in the corresponding
|
||||
* record of the uc_shipments table, plus $shipment->packages, an array
|
||||
* of package objects as returned by uc_shipping_package_load().
|
||||
*
|
||||
* @param $op
|
||||
* The action being taken on the shipment. One of the following values:
|
||||
* - load: The shipment and its packages are loaded from the database.
|
||||
* - save: Changes to the shipment have been written.
|
||||
* - delete: The shipment has been deleted and the packages are available
|
||||
* for reshipment.
|
||||
* @param $shipment
|
||||
* The shipment object.
|
||||
*
|
||||
* @return
|
||||
* Only given when $op is "load". An associative array of extra data to
|
||||
* be added to the shipment object. Each key/value element of the array
|
||||
* becomes a separate member of the shipment object. Elements of the array
|
||||
* with the same key as members of the shipment object replace those members
|
||||
* of the shipment object.
|
||||
*/
|
||||
function hook_uc_shipment($op, $shipment) {
|
||||
switch ($op) {
|
||||
case 'save':
|
||||
$google_order_number = uc_google_checkout_get_google_number($shipment->order_id);
|
||||
if ($google_order_number && $shipment->is_new) {
|
||||
$xml_data = '';
|
||||
foreach ($shipment->packages as $package) {
|
||||
if ($package->tracking_number) {
|
||||
$tracking_number = $package->tracking_number;
|
||||
}
|
||||
elseif ($shipment->tracking_number) {
|
||||
$tracking_number = $shipment->tracking_number;
|
||||
}
|
||||
if ($tracking_number) {
|
||||
foreach ($package->products as $product) {
|
||||
$xml_data .= '<item-shipping-information>';
|
||||
$xml_data .= '<item-id>';
|
||||
$xml_data .= '<merchant-item-id>' . check_plain($product->nid . '|' . $product->model) . '</merchant-item-id>';
|
||||
$xml_data .= '</item-id>';
|
||||
$xml_data .= '<tracking-data-list>';
|
||||
$xml_data .= '<tracking-data>';
|
||||
$xml_data .= '<carrier>' . check_plain($shipment->carrier) . '</carrier>';
|
||||
$xml_data .= '<tracking-number>' . check_plain($tracking_number) . '</tracking-number>';
|
||||
$xml_data .= '</tracking-data>';
|
||||
$xml_data .= '</tracking-data-list>';
|
||||
$xml_data .= '</item-shipping-information>';
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($xml_data) {
|
||||
$request = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
$request .= '<ship-items xmlns="http://checkout.google.com/schema/2" google-order-number="' . $google_order_number . '">';
|
||||
$request .= '<item-shipping-information-list>';
|
||||
$request .= $xml_data;
|
||||
$request .= '</item-shipping-information-list>';
|
||||
$request .= '<send-email>true</send-email>';
|
||||
$request .= '</ship-items>';
|
||||
$response = uc_google_checkout_send_request('request', $request);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
$google_order_number = uc_google_checkout_get_google_number($shipment->order_id);
|
||||
if ($google_order_number) {
|
||||
foreach ($shipment->packages as $package) {
|
||||
foreach ($package->products as $product) {
|
||||
$reset_ids[] = check_plain($product->nid . '|' . $product->model);
|
||||
}
|
||||
}
|
||||
$request = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
$request .= '<reset-items-shipping-information xmlns="http://checkout.google.com/schema/2" google-order-number="' . $google_order_number . '">';
|
||||
$request .= '<item-ids>';
|
||||
foreach (array_unique($reset_ids) as $item_id) {
|
||||
$request .= '<item-id>';
|
||||
$request .= '<merchant-item-id>' . $item_id . '</merchant-item-id>';
|
||||
$request .= '</item-id>';
|
||||
}
|
||||
$request .= '</item-ids>';
|
||||
$request .= '<send-email>false</send-email>';
|
||||
$request .= '</reset-items-shipping-information>';
|
||||
}
|
||||
$response = uc_google_checkout_send_request('request', $request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
@@ -0,0 +1,16 @@
|
||||
name = Shipping
|
||||
description = Gets products ready for physical shipment.
|
||||
dependencies[] = uc_quote
|
||||
package = Ubercart - core (optional)
|
||||
core = 7.x
|
||||
|
||||
; Views handlers
|
||||
files[] = views/uc_shipping_handler_field_shipment_id.inc
|
||||
files[] = views/uc_shipping_handler_field_package_weight.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,520 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_shipping module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_shipping_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_shipments'] = array(
|
||||
'description' => 'Stores shipment information.',
|
||||
'fields' => array(
|
||||
'sid' => array(
|
||||
'description' => 'Primary key: the shipment ID.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id of the order associated with the shipment.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'o_first_name' => array(
|
||||
'description' => 'Origin address: First name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'o_last_name' => array(
|
||||
'description' => 'Origin address: Last name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'o_company' => array(
|
||||
'description' => 'Origin address: Company name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'o_street1' => array(
|
||||
'description' => 'Origin address: Street line 1.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'o_street2' => array(
|
||||
'description' => 'Origin address: Street line 2.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'o_city' => array(
|
||||
'description' => 'Origin address: City.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'o_zone' => array(
|
||||
'description' => 'Origin address: State/province, from {uc_zones}.zone_id.',
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'o_postal_code' => array(
|
||||
'description' => 'Origin address: Postal code.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'o_country' => array(
|
||||
'description' => 'Origin address: Country, from {uc_countries}.country_id.',
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'd_first_name' => array(
|
||||
'description' => 'Destination address: First name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'd_last_name' => array(
|
||||
'description' => 'Destination address: Last name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'd_company' => array(
|
||||
'description' => 'Destination address: Company name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'd_street1' => array(
|
||||
'description' => 'Destination address: Street line 1.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'd_street2' => array(
|
||||
'description' => 'Destination address: Street line 2.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'd_city' => array(
|
||||
'description' => 'Destination address: City.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'd_zone' => array(
|
||||
'description' => 'Destination address: State/province, from {uc_zones}.zone_id.',
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'd_postal_code' => array(
|
||||
'description' => 'Destination address: Postal code.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'd_country' => array(
|
||||
'description' => 'Destination address: Country, from {uc_countries}.country_id.',
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'shipping_method' => array(
|
||||
'description' => 'The shipping method.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'accessorials' => array(
|
||||
'description' => 'Shipping options and special instructions.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'carrier' => array(
|
||||
'description' => 'The company making the delivery.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'transaction_id' => array(
|
||||
'description' => "The carrier's shipment identifier.",
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'tracking_number' => array(
|
||||
'description' => 'The number used by the carrier to locate the shipment while it is in transit.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'ship_date' => array(
|
||||
'description' => 'The Unix timestamp indicating when the shipment left the origin address.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'expected_delivery' => array(
|
||||
'description' => 'The Unix timestamp indicating the expected date of delivery.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'cost' => array(
|
||||
'description' => 'The cost of the shipment.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => TRUE,
|
||||
'default' => 0.0,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp indicating the last time the shipment was modified.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('sid'),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'order_id' => array('order_id'),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_packages'] = array(
|
||||
'description' => 'Stores shipment package information.',
|
||||
'fields' => array(
|
||||
'package_id' => array(
|
||||
'description' => 'Primary key: the package ID.',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'order_id' => array(
|
||||
'description' => 'The {uc_orders}.order_id.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'shipping_type' => array(
|
||||
'description' => 'The basic type of shipment, e.g.: small package, freight, etc.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'pkg_type' => array(
|
||||
'description' => 'The type of packaging.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'length' => array(
|
||||
'description' => 'The package length.',
|
||||
'type' => 'float',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'width' => array(
|
||||
'description' => 'The package width.',
|
||||
'type' => 'float',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'height' => array(
|
||||
'description' => 'The package height.',
|
||||
'type' => 'float',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'length_units' => array(
|
||||
'description' => 'The physical units of the length, width, and height.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'value' => array(
|
||||
'description' => 'The monetary value of the package contents.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 16,
|
||||
'scale' => 5,
|
||||
'not null' => FALSE,
|
||||
'default' => 0.0,
|
||||
),
|
||||
'sid' => array(
|
||||
'description' => 'The {uc_shipments}.sid, if the package has been shipped.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'tracking_number' => array(
|
||||
'description' => 'The package-specific tracking number, if available.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'label_image' => array(
|
||||
'description' => 'The {file}.fid that refers to an image of the shipping label of the package.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('package_id'),
|
||||
'foreign keys' => array(
|
||||
'uc_orders' => array(
|
||||
'table' => 'uc_orders',
|
||||
'columns' => array('order_id' => 'order_id'),
|
||||
),
|
||||
'uc_quote_shipping_types' => array(
|
||||
'table' => 'uc_quote_shipping_types',
|
||||
'columns' => array('shipping_type' => 'shipping_type'),
|
||||
),
|
||||
'uc_shipments' => array(
|
||||
'table' => 'uc_shipments',
|
||||
'columns' => array('sid' => 'sid'),
|
||||
),
|
||||
'file' => array(
|
||||
'table' => 'file',
|
||||
'columns' => array('label_image' => 'fid'),
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'order_id' => array('order_id'),
|
||||
'sid' => array('sid'),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['uc_packaged_products'] = array(
|
||||
'description' => 'Stores packaged product information.',
|
||||
'fields' => array(
|
||||
'package_id' => array(
|
||||
'description' => 'The {uc_packages}.package_id in which the product is shipped.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'order_product_id' => array(
|
||||
'description' => 'The {uc_order_products}.order_product_id of the ordered product.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'qty' => array(
|
||||
'description' => 'The number of this product in this package.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('package_id', 'order_product_id'),
|
||||
'foreign keys' => array(
|
||||
'package_id' => array('uc_packages' => 'package_id'),
|
||||
'order_product_id' => array('uc_order_products' => 'order_product_id'),
|
||||
),
|
||||
'indexes' => array(
|
||||
'order_product_id' => array('order_product_id'),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_shipping_update_last_removed() {
|
||||
return 6005;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes {uc_packages}.label_image to an integer file id.
|
||||
*/
|
||||
function uc_shipping_update_7000(&$sandbox) {
|
||||
$sandbox['#finished'] = 0;
|
||||
|
||||
$schema = array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
);
|
||||
|
||||
if (!isset($sandbox['total'])) {
|
||||
db_add_field('uc_packages', 'label_image_id', $schema);
|
||||
|
||||
$sandbox['last'] = 0;
|
||||
$sandbox['count'] = 0;
|
||||
$sandbox['total'] = db_query("SELECT COUNT(*) FROM {uc_packages} WHERE label_image IS NOT NULL")->fetchField();
|
||||
}
|
||||
else {
|
||||
$found = FALSE;
|
||||
$scheme = variable_get('file_default_scheme', 'public');
|
||||
|
||||
if ($sandbox['total']) {
|
||||
$limit = 200;
|
||||
$images = array();
|
||||
|
||||
$packages = db_query_range("SELECT package_id, label_image FROM {uc_packages} WHERE package_id > :package_id AND label_image IS NOT NULL", 0, $limit, array(':package_id' => $sandbox['last']));
|
||||
|
||||
foreach ($packages as $package) {
|
||||
$found = TRUE;
|
||||
|
||||
$uri = file_stream_wrapper_uri_normalize($scheme . '://' . $package->label_image);
|
||||
|
||||
$fid = db_query("SELECT fid FROM {file_managed} WHERE uri = :uri", array(':uri' => $uri))->fetchField();
|
||||
|
||||
if (!$fid) {
|
||||
$stat = stat($package->label_image);
|
||||
$info = getimagesize($package->label_image);
|
||||
$file = array(
|
||||
'uid' => 1,
|
||||
'filename' => basename($package->label_image),
|
||||
'uri' => $uri,
|
||||
'filemime' => $info['mime'],
|
||||
'filesize' => $stat['size'],
|
||||
'status' => FILE_STATUS_PERMANENT,
|
||||
'timestamp' => $stat['ctime'],
|
||||
);
|
||||
|
||||
$fid = db_insert('file_managed')->fields($file)->execute();
|
||||
}
|
||||
|
||||
db_update('uc_packages')
|
||||
->fields(array('label_image_id' => $fid))
|
||||
->condition('package_id', $package->package_id)
|
||||
->execute();
|
||||
|
||||
$sandbox['last'] = $package->package_id;
|
||||
$sandbox['count']++;
|
||||
$sandbox['message'] = check_plain($package->label_image);
|
||||
}
|
||||
|
||||
$sandbox['#finished'] = min(0.99, $sandbox['count'] / $sandbox['total']);
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
db_drop_field('uc_packages', 'label_image');
|
||||
db_change_field('uc_packages', 'label_image_id', 'label_image', $schema, array(
|
||||
'foreign keys' => array(
|
||||
'label_image' => array('file_managed' => 'fid'),
|
||||
),
|
||||
));
|
||||
|
||||
$sandbox['#finished'] = 1;
|
||||
|
||||
return t('!number label images moved to the file table.', array('!number' => $sandbox['total']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 'changed' column to shipments.
|
||||
*/
|
||||
function uc_shipping_update_7001() {
|
||||
if (!db_field_exists('uc_shipments', 'changed')) {
|
||||
db_add_field('uc_shipments', 'changed', array(
|
||||
'description' => 'The Unix timestamp indicating the last time the shipment was modified.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add indexes to package and shipment tables.
|
||||
*/
|
||||
function uc_shipping_update_7300() {
|
||||
// Alter {uc_shipments} table.
|
||||
if (!db_index_exists('uc_shipments', 'order_id')) {
|
||||
db_add_index('uc_shipments', 'order_id', array('order_id'));
|
||||
}
|
||||
|
||||
// Alter {uc_packages} table.
|
||||
if (!db_index_exists('uc_packages', 'order_id')) {
|
||||
db_add_index('uc_packages', 'order_id', array('order_id'));
|
||||
}
|
||||
if (!db_index_exists('uc_packages', 'sid')) {
|
||||
db_add_index('uc_packages', 'sid', array('sid'));
|
||||
}
|
||||
|
||||
// Alter {uc_packaged_products} table.
|
||||
if (!db_index_exists('uc_packaged_products', 'order_product_id')) {
|
||||
db_add_index('uc_packaged_products', 'order_product_id', array('order_product_id'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove orphaned package and shipment records.
|
||||
*/
|
||||
function uc_shipping_update_7301() {
|
||||
$subquery = db_select('uc_orders', 'o')
|
||||
->fields('o', array('order_id'));
|
||||
|
||||
db_delete('uc_shipments')
|
||||
->condition('order_id', $subquery, 'NOT IN')
|
||||
->execute();
|
||||
|
||||
db_delete('uc_packages')
|
||||
->condition('order_id', $subquery, 'NOT IN')
|
||||
->execute();
|
||||
|
||||
$subquery = db_select('uc_packages', 'p')
|
||||
->fields('p', array('package_id'));
|
||||
|
||||
db_delete('uc_packaged_products')
|
||||
->condition('package_id', $subquery, 'NOT IN')
|
||||
->execute();
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @file
|
||||
* Adds autofill address functionality to shipment forms.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Autofills shipment address form from user selection.
|
||||
*
|
||||
* @param type
|
||||
* Field prefix used to identify the address.
|
||||
* @param json_address
|
||||
* JSON object of address data.
|
||||
*/
|
||||
function apply_address(type, json_address) {
|
||||
//if (json_address != "0") {
|
||||
eval("var address = " + json_address +";");
|
||||
jQuery('#edit-' + type + '-first-name').val(address.first_name);
|
||||
jQuery('#edit-' + type + '-last-name').val(address.last_name);
|
||||
jQuery('#edit-' + type + '-phone').val(address.phone);
|
||||
jQuery('#edit-' + type + '-company').val(address.company);
|
||||
jQuery('#edit-' + type + '-street1').val(address.street1);
|
||||
jQuery('#edit-' + type + '-street2').val(address.street2);
|
||||
jQuery('#edit-' + type + '-city').val(address.city);
|
||||
jQuery('#edit-' + type + '-postal-code').val(address.postal_code);
|
||||
|
||||
if (jQuery('#edit-' + type + '-country').val() != address.country) {
|
||||
jQuery('#edit-' + type + '-country').val(address.country);
|
||||
}
|
||||
|
||||
jQuery('#edit-' + type + '-zone').val(address.zone);
|
||||
//}
|
||||
}
|
@@ -0,0 +1,882 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Organizes ordered products into packages and sets them up for shipment.
|
||||
* Shipping method modules may add functionality to generate shipping labels
|
||||
* and tracking numbers.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function uc_shipping_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/store/orders/%/packages/new':
|
||||
return '<p>' . t('Organize products into packages. Package numbers in multiple shipping types are of the first shipping type they appear in. All packages are given a unique ID when they are saved. Choose the default package "Sep." to automatically create a package for each of the selected quantity of products in that row.') . '</p>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function uc_shipping_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['admin/store/orders/%uc_order/packages'] = array(
|
||||
'title' => 'Packages',
|
||||
'page callback' => 'uc_shipping_order_packages',
|
||||
'page arguments' => array(3),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'weight' => 6,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/packages/new'] = array(
|
||||
'title' => 'Create packages',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_shipping_new_package', 3),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'type' => MENU_LOCAL_ACTION,
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/packages/%uc_shipping_package/edit'] = array(
|
||||
'title' => 'Edit package',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_shipping_package_edit', 3, 5),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/packages/%uc_shipping_package/cancel'] = array(
|
||||
'title' => 'Cancel package shipment',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_shipping_package_cancel_confirm', 3, 5),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/packages/%uc_shipping_package/delete'] = array(
|
||||
'title' => 'Delete package',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_shipping_package_delete_confirm', 3, 5),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments'] = array(
|
||||
'title' => 'Shipments',
|
||||
'page callback' => 'uc_shipping_order_shipments',
|
||||
'page arguments' => array(3),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'weight' => 7,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments/new'] = array(
|
||||
'title' => 'New shipment',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_shipping_new_shipment', 3),
|
||||
'access callback' => 'uc_shipping_new_shipment_access',
|
||||
'access arguments' => array(3),
|
||||
'type' => MENU_LOCAL_ACTION,
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment'] = array(
|
||||
'title callback' => 'uc_shipping_shipment_page_title',
|
||||
'title arguments' => array(5),
|
||||
'page callback' => 'uc_shipping_shipment_view',
|
||||
'page arguments' => array(3, 5),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/view'] = array(
|
||||
'title' => 'View',
|
||||
'weight' => -5,
|
||||
'type' => MENU_DEFAULT_LOCAL_TASK,
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/edit'] = array(
|
||||
'title' => 'Edit',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_shipping_shipment_edit', 3, 5),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'weight' => -1,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/print'] = array(
|
||||
'title' => 'Print',
|
||||
'page callback' => 'uc_shipping_shipment_print',
|
||||
'page arguments' => array(3, 5),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/packing_slip'] = array(
|
||||
'title' => 'Packing slip',
|
||||
'page callback' => 'uc_shipping_shipment_print',
|
||||
'page arguments' => array(3, 5, FALSE),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/delete'] = array(
|
||||
'title' => 'Delete shipment',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_shipping_shipment_delete_confirm', 3, 5),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
$items['admin/store/orders/%uc_order/ship'] = array(
|
||||
'title' => 'Ship packages',
|
||||
'page callback' => 'uc_shipping_make_shipment',
|
||||
'page arguments' => array(3),
|
||||
'access callback' => 'uc_shipping_order_access',
|
||||
'access arguments' => array(3),
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for admin/store/orders/%/shipments/%.
|
||||
*/
|
||||
function uc_shipping_shipment_page_title($shipment) {
|
||||
return t('Shipment !id', array('!id' => $shipment->sid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures access to the Shipments tab.
|
||||
*/
|
||||
function uc_shipping_order_access($order) {
|
||||
return user_access('fulfill orders') && uc_order_is_shippable($order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for the new shipment page.
|
||||
*/
|
||||
function uc_shipping_new_shipment_access($order) {
|
||||
return uc_shipping_order_access($order) && db_query("SELECT COUNT(*) FROM {uc_packages} WHERE order_id = :id AND sid IS NULL", array(':id' => $order->order_id))->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_local_tasks_alter().
|
||||
*/
|
||||
function uc_shipping_menu_local_tasks_alter(&$data, $router_item, $root_path) {
|
||||
if ($root_path == 'admin/store/orders/%/shipments') {
|
||||
$order = $router_item['page_arguments'][0];
|
||||
$item = menu_get_item('admin/store/orders/' . $order->order_id . '/packages/new');
|
||||
if ($item['access']) {
|
||||
$data['actions']['output'][] = array(
|
||||
'#theme' => 'menu_local_action',
|
||||
'#link' => $item,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_admin_paths().
|
||||
*/
|
||||
function uc_shipping_admin_paths() {
|
||||
return array(
|
||||
// Don't show packing slips with the admin theme, overlay, etc.
|
||||
'admin/store/orders/*/shipments/*/print' => FALSE,
|
||||
'admin/store/orders/*/shipments/*/packing_slip' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function uc_shipping_permission() {
|
||||
return array(
|
||||
'fulfill orders' => array(
|
||||
'title' => t('Fulfill orders'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function uc_shipping_theme() {
|
||||
return array(
|
||||
'uc_shipping_new_package_fieldset' => array(
|
||||
'render element' => 'fieldset',
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
),
|
||||
'uc_shipping_edit_package_fieldset' => array(
|
||||
'render element' => 'fieldset',
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
),
|
||||
'uc_shipping_new_shipment' => array(
|
||||
'render element' => 'form',
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
),
|
||||
'uc_shipping_shipment_print' => array(
|
||||
'variables' => array('order' => NULL, 'shipment' => NULL, 'labels' => TRUE),
|
||||
'file' => 'uc_shipping.admin.inc',
|
||||
),
|
||||
'uc_packing_slip' => array(
|
||||
'variables' => array('order' => NULL, 'shipment' => NULL),
|
||||
'template' => 'uc-packing-slip',
|
||||
),
|
||||
'uc_packing_slip_page' => array(
|
||||
'variables' => array('content' => NULL),
|
||||
'template' => 'uc-packing-slip-page',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess function to make tokens available in the packing slip template.
|
||||
*
|
||||
* @see uc-packing-slip.tpl.php
|
||||
*/
|
||||
function template_preprocess_uc_packing_slip(&$variables) {
|
||||
$tokens = token_generate('site', drupal_map_assoc(array('logo')));
|
||||
$variables['site_logo'] = isset($tokens['logo']) ? $tokens['logo'] : '';
|
||||
|
||||
$tokens = token_generate('store', drupal_map_assoc(array('name', 'address', 'phone')));
|
||||
$variables['store_name'] = $tokens['name'];
|
||||
$variables['store_address'] = $tokens['address'];
|
||||
$variables['store_phone'] = $tokens['phone'];
|
||||
|
||||
$order = $variables['order'];
|
||||
$variables['order_link'] = l($order->order_id, url('user/' . $order->uid . '/orders/' . $order->order_id, array('absolute' => TRUE)));
|
||||
$variables['order_email'] = check_plain($order->primary_email);
|
||||
$variables['billing_address'] = uc_order_address($order, 'billing');
|
||||
$variables['billing_phone'] = check_plain($order->billing_phone);
|
||||
$variables['shipping_address'] = uc_order_address($order, 'delivery');
|
||||
$variables['shipping_phone'] = check_plain($order->delivery_phone);
|
||||
|
||||
if (module_exists('uc_payment')) {
|
||||
$payment_method = _uc_payment_method_data($order->payment_method, 'review');
|
||||
if (empty($payment_method)) {
|
||||
$payment_method = _uc_payment_method_data($order->payment_method, 'name');
|
||||
}
|
||||
$variables['payment_method'] = $payment_method;
|
||||
}
|
||||
else {
|
||||
$variables['payment_method'] = '';
|
||||
}
|
||||
|
||||
$shipment = $variables['shipment'];
|
||||
$variables['carrier'] = check_plain($shipment->carrier);
|
||||
$variables['tracking_number'] = check_plain($shipment->tracking_number);
|
||||
$variables['packages'] = $shipment->packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocesses a printable packing slip page.
|
||||
*
|
||||
* @see uc-packing-slip-page.tpl.php
|
||||
*/
|
||||
function template_preprocess_uc_packing_slip_page(&$variables) {
|
||||
$language = isset($GLOBALS['language']) ? $GLOBALS['language'] : language_default();
|
||||
$variables['language'] = $language;
|
||||
$variables['language']->dir = $language->direction ? 'rtl' : 'ltr';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_order_pane().
|
||||
*/
|
||||
function uc_shipping_uc_order_pane() {
|
||||
$panes['packages'] = array(
|
||||
'callback' => 'uc_shipping_order_pane_packages',
|
||||
'title' => t('Tracking numbers'),
|
||||
'desc' => t('Display tracking numbers of shipped packages.'),
|
||||
'class' => 'pos-left',
|
||||
'weight' => 7,
|
||||
'show' => array('view', 'invoice', 'customer'),
|
||||
);
|
||||
|
||||
return $panes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_order_actions().
|
||||
*/
|
||||
function uc_shipping_uc_order_actions($order) {
|
||||
$actions = array();
|
||||
if (user_access('fulfill orders')) {
|
||||
$result = db_query("SELECT COUNT(nid) FROM {uc_order_products} WHERE order_id = :id AND data LIKE :data", array(':id' => $order->order_id, ':data' => '%s:9:\"shippable\";s:1:\"1\";%'));
|
||||
if ($result->fetchField()) {
|
||||
$title = t('Package order !order_id products.', array('!order_id' => $order->order_id));
|
||||
$actions[] = array(
|
||||
'name' => t('Package'),
|
||||
'url' => 'admin/store/orders/' . $order->order_id . '/packages',
|
||||
'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_shipping') . '/images/package.gif', 'alt' => $title)),
|
||||
'title' => $title,
|
||||
);
|
||||
$result = db_query("SELECT COUNT(package_id) FROM {uc_packages} WHERE order_id = :id", array(':id' => $order->order_id));
|
||||
if ($result->fetchField()) {
|
||||
$title = t('Ship order !order_id packages.', array('!order_id' => $order->order_id));
|
||||
$actions[] = array(
|
||||
'name' => t('Ship'),
|
||||
'url' => 'admin/store/orders/' . $order->order_id . '/shipments',
|
||||
'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_shipping') . '/images/ship.gif', 'alt' => $title)),
|
||||
'title' => $title,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the details of a package.
|
||||
*/
|
||||
function uc_shipping_package_view($package) {
|
||||
$shipment = uc_shipping_shipment_load($package->sid);
|
||||
$build = array(
|
||||
'#prefix' => '<div class="order-pane pos-left">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$rows = array();
|
||||
|
||||
$build['title'] = array(
|
||||
'#markup' => t('Package %id:', array('%id' => $package->package_id)),
|
||||
'#prefix' => '<div class="order-pane-title">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
|
||||
$rows[] = array(t('Contents:'), filter_xss_admin($package->description));
|
||||
|
||||
if ($shipment) {
|
||||
$methods = module_invoke_all('uc_shipping_method');
|
||||
if (isset($methods[$shipment->shipping_method])) {
|
||||
$pkg_type = $methods[$shipment->shipping_method]['ship']['pkg_types'][$package->pkg_type];
|
||||
}
|
||||
}
|
||||
|
||||
$rows[] = array(t('Package type:'), isset($pkg_type) ? $pkg_type : check_plain($package->pkg_type));
|
||||
|
||||
if ($package->length && $package->width && $package->height) {
|
||||
$rows[] = array(t('Dimensions:'), t('!l x !w x !h', array('!l' => uc_length_format($package->length), '!w' => uc_length_format($package->width), '!h' => uc_length_format($package->height))));
|
||||
}
|
||||
|
||||
$rows[] = array(t('Insured value:'), array('data' => array('#theme' => 'uc_price', '#price' => $package->value)));
|
||||
|
||||
if ($package->tracking_number) {
|
||||
$rows[] = array(t('Tracking number:'), check_plain($package->tracking_number));
|
||||
}
|
||||
|
||||
if ($shipment && isset($package->label_image) &&
|
||||
file_exists($package->label_image->uri)) {
|
||||
$rows[] = array(t('Label:'), l(t('Click to view.'), 'admin/store/orders/' . $package->order_id . '/shipments/labels/' . $shipment->shipping_method . '/' . $package->label_image->uri));
|
||||
}
|
||||
else {
|
||||
$rows[] = array(t('Label:'), t('n/a'));
|
||||
}
|
||||
|
||||
$build['package'] = array(
|
||||
'#theme' => 'table',
|
||||
'#rows' => $rows,
|
||||
'attributes' => array('style' => 'width:auto;'),
|
||||
);
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a package and its products.
|
||||
*/
|
||||
function uc_shipping_package_load($package_id) {
|
||||
static $packages = array();
|
||||
|
||||
if (!isset($packages[$package_id])) {
|
||||
$result = db_query("SELECT * FROM {uc_packages} WHERE package_id = :id", array(':id' => $package_id));
|
||||
if ($package = $result->fetchObject()) {
|
||||
$products = array();
|
||||
$description = '';
|
||||
$weight = 0;
|
||||
$units = variable_get('uc_weight_unit', 'lb');
|
||||
$addresses = array();
|
||||
$result = db_query("SELECT op.order_product_id, pp.qty, pp.qty * op.weight AS weight, op.weight_units, op.nid, op.title, op.model, op.price, op.data FROM {uc_packaged_products} pp LEFT JOIN {uc_order_products} op ON op.order_product_id = pp.order_product_id WHERE pp.package_id = :id ORDER BY op.order_product_id", array(':id' => $package_id));
|
||||
foreach ($result as $product) {
|
||||
$address = uc_quote_get_default_shipping_address($product->nid);
|
||||
// TODO: Lodge complaint that array_unique() compares as strings.
|
||||
if (!in_array($address, $addresses)) {
|
||||
$addresses[] = $address;
|
||||
}
|
||||
$description .= ', ' . $product->qty . ' x ' . $product->model;
|
||||
// Normalize all weights to default units.
|
||||
$weight += $product->weight * uc_weight_conversion($product->weight_units, $units);
|
||||
$product->data = unserialize($product->data);
|
||||
$products[$product->order_product_id] = $product;
|
||||
}
|
||||
$package->addresses = $addresses;
|
||||
$package->description = substr($description, 2);
|
||||
$package->weight = $weight;
|
||||
$package->weight_units = $units;
|
||||
$package->products = $products;
|
||||
|
||||
if ($package->label_image && $image = file_load($package->label_image)) {
|
||||
$package->label_image = $image;
|
||||
}
|
||||
else {
|
||||
unset($package->label_image);
|
||||
}
|
||||
|
||||
$packages[$package_id] = $package;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return $packages[$package_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a package.
|
||||
*/
|
||||
function uc_shipping_package_save($package) {
|
||||
$package = (object)$package;
|
||||
|
||||
if (!isset($package->package_id)) {
|
||||
$package->package_id = db_insert('uc_packages')
|
||||
->fields(array('order_id' => $package->order_id))
|
||||
->execute();
|
||||
}
|
||||
|
||||
if (isset($package->products) && $package->products) {
|
||||
$insert = db_insert('uc_packaged_products')
|
||||
->fields(array('package_id', 'order_product_id', 'qty'));
|
||||
|
||||
foreach ($package->products as $id => $product) {
|
||||
$insert->values(array(
|
||||
'package_id' => $package->package_id,
|
||||
'order_product_id' => $id,
|
||||
'qty' => $product->qty,
|
||||
));
|
||||
|
||||
$result = db_query("SELECT data FROM {uc_order_products} WHERE order_product_id = :id", array(':id' => $id));
|
||||
if ($order_product = $result->fetchObject()) {
|
||||
$order_product->data = unserialize($order_product->data);
|
||||
$order_product->data['package_id'] = intval($package->package_id);
|
||||
|
||||
db_update('uc_order_products')
|
||||
->fields(array('data' => serialize($order_product->data)))
|
||||
->condition('order_product_id', $id)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
db_delete('uc_packaged_products')
|
||||
->condition('package_id', $package->package_id)
|
||||
->execute();
|
||||
|
||||
$insert->execute();
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
'order_id' => $package->order_id,
|
||||
'shipping_type' => $package->shipping_type,
|
||||
);
|
||||
|
||||
if (isset($package->pkg_type)) {
|
||||
$fields['pkg_type'] = $package->pkg_type;
|
||||
}
|
||||
if (isset($package->length) && isset($package->width) && isset($package->height) && isset($package->length_units)) {
|
||||
$fields['length'] = $package->length;
|
||||
$fields['width'] = $package->width;
|
||||
$fields['height'] = $package->height;
|
||||
$fields['length_units'] = $package->length_units;
|
||||
}
|
||||
if (isset($package->value)) {
|
||||
$fields['value'] = $package->value;
|
||||
}
|
||||
if (isset($package->sid)) {
|
||||
$fields['sid'] = $package->sid;
|
||||
}
|
||||
if (isset($package->tracking_number)) {
|
||||
$fields['tracking_number'] = $package->tracking_number;
|
||||
}
|
||||
if (isset($package->label_image) && is_object($package->label_image)) {
|
||||
$fields['label_image'] = $package->label_image->fid;
|
||||
}
|
||||
|
||||
db_update('uc_packages')
|
||||
->fields($fields)
|
||||
->condition('package_id', $package->package_id)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a package.
|
||||
*/
|
||||
function uc_shipping_package_delete($package_id) {
|
||||
// @todo: Make these delete functions take the actual object.
|
||||
$package = uc_shipping_package_load($package_id);
|
||||
|
||||
db_delete('uc_packages')
|
||||
->condition('package_id', $package_id)
|
||||
->execute();
|
||||
db_delete('uc_packaged_products')
|
||||
->condition('package_id', $package_id)
|
||||
->execute();
|
||||
|
||||
if (isset($package->label_image)) {
|
||||
file_usage_delete($package->label_image, 'uc_shipping', 'package', $package_id);
|
||||
file_delete($package->label_image);
|
||||
}
|
||||
|
||||
drupal_set_message(t('Package @id has been deleted.', array('@id' => $package_id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a shipment and its packages.
|
||||
*/
|
||||
function uc_shipping_shipment_load($shipment_id) {
|
||||
$shipment = db_query("SELECT * FROM {uc_shipments} WHERE sid = :sid", array(':sid' => $shipment_id))->fetchObject();
|
||||
if ($shipment) {
|
||||
$result = db_query("SELECT package_id FROM {uc_packages} WHERE sid = :sid", array(':sid' => $shipment_id));
|
||||
$packages = array();
|
||||
foreach ($result as $package) {
|
||||
$packages[$package->package_id] = uc_shipping_package_load($package->package_id);
|
||||
}
|
||||
$shipment->packages = $packages;
|
||||
|
||||
$extra = module_invoke_all('uc_shipment', 'load', $shipment);
|
||||
if (is_array($extra)) {
|
||||
foreach ($extra as $key => $value) {
|
||||
$shipment->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $shipment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a shipment.
|
||||
*/
|
||||
function uc_shipping_shipment_save($shipment) {
|
||||
if (isset($shipment->origin)) {
|
||||
foreach ($shipment->origin as $field => $value) {
|
||||
$field = 'o_' . $field;
|
||||
$shipment->$field = $value;
|
||||
$fields[$field] = $value;
|
||||
}
|
||||
}
|
||||
if (isset($shipment->destination)) {
|
||||
foreach ($shipment->destination as $field => $value) {
|
||||
$field = 'd_' . $field;
|
||||
$shipment->$field = $value;
|
||||
$fields[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$shipment->changed = time();
|
||||
|
||||
if (!isset($shipment->sid)) {
|
||||
drupal_write_record('uc_shipments', $shipment);
|
||||
$shipment->is_new = TRUE;
|
||||
}
|
||||
else {
|
||||
drupal_write_record('uc_shipments', $shipment, 'sid');
|
||||
$shipment->is_new = FALSE;
|
||||
}
|
||||
|
||||
if (is_array($shipment->packages)) {
|
||||
foreach ($shipment->packages as $package) {
|
||||
$package->sid = $shipment->sid;
|
||||
// Since the products haven't changed, we take them out of the object so
|
||||
// that they are not deleted and re-inserted.
|
||||
$products = $package->products;
|
||||
unset($package->products);
|
||||
uc_shipping_package_save($package);
|
||||
// But they're still necessary for hook_uc_shipment(), so they're added
|
||||
// back in.
|
||||
$package->products = $products;
|
||||
}
|
||||
}
|
||||
|
||||
module_invoke_all('uc_shipment', 'save', $shipment);
|
||||
$order = uc_order_load($shipment->order_id);
|
||||
rules_invoke_event('uc_shipment_save', $order, $shipment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a shipment.
|
||||
*/
|
||||
function uc_shipping_shipment_delete($shipment_id) {
|
||||
$shipment = uc_shipping_shipment_load($shipment_id);
|
||||
|
||||
db_update('uc_packages')
|
||||
->fields(array(
|
||||
'sid' => NULL,
|
||||
'tracking_number' => NULL,
|
||||
'label_image' => NULL,
|
||||
))
|
||||
->condition('sid', $shipment_id)
|
||||
->execute();
|
||||
|
||||
db_delete('uc_shipments')
|
||||
->condition('sid', $shipment_id)
|
||||
->execute();
|
||||
|
||||
foreach ($shipment->packages as $package) {
|
||||
if (isset($package->label_image)) {
|
||||
file_delete($package->label_image);
|
||||
unset($package->label_image);
|
||||
}
|
||||
}
|
||||
|
||||
module_invoke_all('uc_shipment', 'delete', $shipment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shipping order pane callback.
|
||||
*
|
||||
* @see uc_shipping_uc_order_pane()
|
||||
*/
|
||||
function uc_shipping_order_pane_packages($op, $order) {
|
||||
switch ($op) {
|
||||
case 'view':
|
||||
case 'customer':
|
||||
$tracking = array();
|
||||
$result = db_query("SELECT sid FROM {uc_shipments} WHERE order_id = :id", array(':id' => $order->order_id));
|
||||
foreach ($result as $shipment) {
|
||||
$shipment = uc_shipping_shipment_load($shipment->sid);
|
||||
if ($shipment->tracking_number) {
|
||||
$tracking[$shipment->carrier]['data'] = $shipment->carrier;
|
||||
$tracking[$shipment->carrier]['children'][] = check_plain($shipment->tracking_number);
|
||||
}
|
||||
else {
|
||||
foreach ($shipment->packages as $package) {
|
||||
if ($package->tracking_number) {
|
||||
$tracking[$shipment->carrier]['data'] = $shipment->carrier;
|
||||
$tracking[$shipment->carrier]['children'][] = check_plain($package->tracking_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not show an empty pane to customers.
|
||||
if ($op == 'view' || !empty($tracking)) {
|
||||
$build['tracking'] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $tracking,
|
||||
);
|
||||
|
||||
return $build;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses an address to fill out a form.
|
||||
*/
|
||||
function uc_shipping_select_address($addresses, $onchange = '', $title = NULL) {
|
||||
if (!is_array($addresses) || count($addresses) == 0) {
|
||||
$addresses = array();
|
||||
}
|
||||
$store_address = variable_get('uc_quote_store_default_address', new UcAddress());
|
||||
if (!in_array($store_address, $addresses)) {
|
||||
$addresses[] = $store_address;
|
||||
}
|
||||
|
||||
$blank = array(
|
||||
'first_name' => '',
|
||||
'last_name' => '',
|
||||
'phone' => '',
|
||||
'company' => '',
|
||||
'street1' => '',
|
||||
'street2' => '',
|
||||
'city' => '',
|
||||
'postal_code' => '',
|
||||
'country' => 0,
|
||||
'zone' => 0,
|
||||
);
|
||||
$options = array(drupal_json_encode($blank) => t('- Reset fields -'));
|
||||
foreach ($addresses as $address) {
|
||||
$options[drupal_json_encode($address)] = $address->company . ' ' . $address->street1 . ' ' . $address->city;
|
||||
}
|
||||
|
||||
$select = array(
|
||||
'#type' => 'select',
|
||||
'#title' => is_null($title) ? t('Address book') : $title,
|
||||
'#options' => $options,
|
||||
'#default_value' => drupal_json_encode($addresses[0]),
|
||||
'#attributes' => array('onchange' => $onchange),
|
||||
);
|
||||
|
||||
return $select;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for addresses in forms.
|
||||
*
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_shipping_address_form($form, &$form_state, $addresses, $order) {
|
||||
drupal_add_js(drupal_get_path('module', 'uc_shipping') . '/uc_shipping.js');
|
||||
|
||||
$form['origin'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Origin address'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
'#weight' => -2,
|
||||
);
|
||||
$form['origin']['pickup_address_select'] = uc_shipping_select_address($addresses, 'apply_address(\'pickup\', this.value);', t('Saved Addresses'));
|
||||
$form['origin']['pickup_address_select']['#weight'] = -2;
|
||||
$form['origin']['pickup_email'] = uc_textfield(t('E-mail'), uc_store_email(), FALSE, NULL, 255);
|
||||
$form['origin']['pickup_email']['#weight'] = -1;
|
||||
$form['origin']['pickup_address']['#tree'] = TRUE;
|
||||
$form['origin']['pickup_address']['pickup_address'] = array(
|
||||
'#type' => 'uc_address',
|
||||
'#default_value' => reset($addresses),
|
||||
'#required' => FALSE,
|
||||
);
|
||||
|
||||
$form['destination'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Destination address'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => FALSE,
|
||||
'#weight' => -1,
|
||||
);
|
||||
if (isset($form_state['values']['delivery_country'])) {
|
||||
$order->delivery_country = $form_state['values']['delivery_country'];
|
||||
}
|
||||
$form['destination']['delivery_email'] = uc_textfield(t('E-mail'), $order->primary_email, FALSE, NULL, 255);
|
||||
$form['destination']['delivery_email']['#weight'] = -1;
|
||||
$form['destination']['delivery_address'] = array(
|
||||
'#type' => 'uc_address',
|
||||
'#default_value' => $order,
|
||||
'#required' => FALSE,
|
||||
'#key_prefix' => 'delivery',
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
function uc_shipping_views_api() {
|
||||
return array(
|
||||
'api' => '2.0',
|
||||
'path' => drupal_get_path('module', 'uc_shipping') . '/views',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_date_views_tables().
|
||||
*/
|
||||
function uc_shipping_date_views_tables() {
|
||||
return array('uc_shipments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_date_views_fields().
|
||||
*
|
||||
* All modules that create custom fields that use the
|
||||
* 'views_handler_field_date' handler can provide
|
||||
* additional information here about the type of
|
||||
* date they create so the date can be used by
|
||||
* the Date API views date argument and date filter.
|
||||
*/
|
||||
function uc_shipping_date_views_fields($field) {
|
||||
$values = array(
|
||||
// The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
|
||||
'sql_type' => DATE_UNIX,
|
||||
// Timezone handling options: 'none', 'site', 'date', 'utc' .
|
||||
'tz_handling' => 'site',
|
||||
// Needed only for dates that use 'date' tz_handling.
|
||||
'timezone_field' => '',
|
||||
// Needed only for dates that use 'date' tz_handling.
|
||||
'offset_field' => '',
|
||||
// Array of "table.field" values for related fields that should be
|
||||
// loaded automatically in the Views SQL.
|
||||
'related_fields' => array(),
|
||||
// Granularity of this date field's db data.
|
||||
'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
|
||||
);
|
||||
|
||||
switch ($field) {
|
||||
case 'uc_shipments.ship_date':
|
||||
case 'uc_shipments.expected_delivery':
|
||||
case 'uc_shipments.changed':
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_order().
|
||||
*
|
||||
* Prevent users from deleting orders with a shipment or package that has
|
||||
* a tracking number, unless the user has administrative privileges or the
|
||||
* "Unconditionally delete orders" permission.
|
||||
*
|
||||
* Delete packages and shipments attached to orders being deleted.
|
||||
*/
|
||||
function uc_shipping_uc_order($op, $order, $arg2) {
|
||||
switch ($op) {
|
||||
case 'can_delete':
|
||||
// Find and check the shipments for tracking numbers.
|
||||
// {uc_shipments}.tracking_number is NOT NULL.
|
||||
$shipment_count = db_select('uc_shipments')
|
||||
->condition('order_id', $order->order_id)
|
||||
->condition('tracking_number', '', '<>')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
if ($shipment_count > 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Find and check the packages.
|
||||
$package_count = db_select('uc_packages')
|
||||
->condition('order_id', $order->order_id)
|
||||
->isNotNull('tracking_number')
|
||||
->condition('tracking_number', '', '<>')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
if ($package_count > 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
// Find and delete the shipments.
|
||||
$shipment_ids = db_select('uc_shipments')
|
||||
->fields(NULL, array('sid'))
|
||||
->condition('order_id', $order->order_id)
|
||||
->execute()
|
||||
->fetchCol();
|
||||
foreach ($shipment_ids as $sid) {
|
||||
uc_shipping_shipment_delete($sid);
|
||||
}
|
||||
|
||||
// Find and delete the packages.
|
||||
$package_ids = db_select('uc_packages')
|
||||
->fields(NULL, array('package_id'))
|
||||
->condition('order_id', $order->order_id)
|
||||
->execute()
|
||||
->fetchCol();
|
||||
foreach ($package_ids as $pid) {
|
||||
uc_shipping_package_delete($pid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules hooks for uc_shipping.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_rules_data_info().
|
||||
*/
|
||||
function uc_shipping_rules_data_info() {
|
||||
$address_info = uc_address_property_info();
|
||||
|
||||
$entities['uc_shipment'] = array(
|
||||
'label' => t('Ubercart shipment object'),
|
||||
'group' => t('Ubercart'),
|
||||
'wrap' => TRUE,
|
||||
'property info' => array(
|
||||
'sid' => array(
|
||||
'type' => 'integer',
|
||||
'label' => t('Shipment ID'),
|
||||
),
|
||||
'order-id' => array(
|
||||
'type' => 'integer',
|
||||
'label' => t('Order ID'),
|
||||
),
|
||||
'origin' => array(
|
||||
'type' => 'struct',
|
||||
'label' => t('Origin address'),
|
||||
'description' => t('The origin location for the shipment.'),
|
||||
'getter callback' => 'uc_shipping_address_property_get',
|
||||
'setter callback' => 'uc_shipping_address_property_set',
|
||||
'setter permission' => 'fulfill orders',
|
||||
'property info' => $address_info,
|
||||
),
|
||||
'destination' => array(
|
||||
'type' => 'struct',
|
||||
'label' => t('Destination address'),
|
||||
'description' => t('The destination location for the shipment.'),
|
||||
'getter callback' => 'uc_shipping_address_property_get',
|
||||
'setter callback' => 'uc_shipping_address_property_set',
|
||||
'setter permission' => 'fulfill orders',
|
||||
'property info' => $address_info,
|
||||
),
|
||||
'shipping-method' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Shipping method'),
|
||||
'description' => t('The transportation method used to ship.'),
|
||||
),
|
||||
'accessorials' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Accessorials'),
|
||||
'description' => t('Shipping options and special instructions.'),
|
||||
),
|
||||
'carrier' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Carrier'),
|
||||
'description' > t('The company making the delivery.'),
|
||||
),
|
||||
'transaction-id' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Transaction ID'),
|
||||
'description' => t("The carrier's shipment identifier."),
|
||||
),
|
||||
'tracking-number' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Tracking number'),
|
||||
'description' => t('The number used by the carrier to locate the shipment while it is in transit.'),
|
||||
),
|
||||
'ship-date' => array(
|
||||
'type' => 'date',
|
||||
'label' => t('Ship date'),
|
||||
'description' => t('The time the shipment was sent out.'),
|
||||
),
|
||||
'expected-delivery' => array(
|
||||
'type' => 'date',
|
||||
'label' => t('Expected delivery'),
|
||||
'description' => t('The time the shipment is expected to be delivered'),
|
||||
),
|
||||
'cost' => array(
|
||||
'type' => 'decimal',
|
||||
'label' => t('Cost'),
|
||||
'description' => t('The cost of the shipment.'),
|
||||
),
|
||||
'packages' => array(
|
||||
'type' => 'list<struct>',
|
||||
'label' => t('Packages'),
|
||||
'description' => t('The physical items being shipped.'),
|
||||
'property info' => array(
|
||||
'package-id' => array(
|
||||
'type' => 'integer',
|
||||
'label' => t('Package ID'),
|
||||
),
|
||||
'shipping-type' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Shipping type'),
|
||||
'description' => t('The basic type of shipment, e.g.: small package, freight, etc.'),
|
||||
),
|
||||
'pkg-type' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Package type'),
|
||||
'description' => t('The type of packaging.'),
|
||||
),
|
||||
'length' => array(
|
||||
'type' => 'decimal',
|
||||
'label' => t('Length'),
|
||||
'description' => t('The package length.'),
|
||||
),
|
||||
'width' => array(
|
||||
'type' => 'decimal',
|
||||
'label' => t('Width'),
|
||||
'description' => t('The package width.'),
|
||||
),
|
||||
'height' => array(
|
||||
'type' => 'decimal',
|
||||
'label' => t('Height'),
|
||||
'description' => t('The package height.'),
|
||||
),
|
||||
'value' => array(
|
||||
'type' => 'decimal',
|
||||
'label' => t('Value'),
|
||||
'description' => t('The monetary value of the package contents.'),
|
||||
),
|
||||
'tracking-number' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Tracking number'),
|
||||
'description' => t('The number used by the carrier to locate the shipment while it is in transit.'),
|
||||
),
|
||||
'description' => array(
|
||||
'type' => 'text',
|
||||
'label' => t('Description'),
|
||||
'description' => t('The package description'),
|
||||
),
|
||||
'weight' => array(
|
||||
'type' => 'decimal',
|
||||
'label' => t('Weight'),
|
||||
'description' => t('The physical weight of the package.'),
|
||||
),
|
||||
'products' => array(
|
||||
'type' => 'list<uc_order_product>',
|
||||
'label' => t('Products'),
|
||||
'description' => t('The package contents.'),
|
||||
),
|
||||
'label-image' => array(
|
||||
'type' => 'file',
|
||||
'label' => t('Label image'),
|
||||
'description' => t('An image of the shipping label.'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity metadata callback to get origin or destination address of an shipment.
|
||||
*/
|
||||
function uc_shipping_address_property_get($shipment, array $options, $name, $entity_type) {
|
||||
switch ($name) {
|
||||
case 'origin':
|
||||
$type = 'o_';
|
||||
break;
|
||||
|
||||
case 'destination':
|
||||
$type = 'd_';
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$address = new UcAddress();
|
||||
|
||||
foreach ($address as $field => $value) {
|
||||
$address->{$field} = $shipment->{$type . $field};
|
||||
}
|
||||
|
||||
return $address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity metadata callback to set origin or destination address of an order.
|
||||
*/
|
||||
function uc_shipping_address_property_set($shipment, $name, $address) {
|
||||
switch ($name) {
|
||||
case 'origin':
|
||||
$type = 'o_';
|
||||
break;
|
||||
|
||||
case 'destination':
|
||||
$type = 'd_';
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($address as $field => $value) {
|
||||
$shipment->{$type . $field} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rules_event_info().
|
||||
*/
|
||||
function uc_shipping_rules_event_info() {
|
||||
$events['uc_shipment_save'] = array(
|
||||
'label' => t('A shipment is saved'),
|
||||
'group' => t('Fulfillment'),
|
||||
'variables' => array(
|
||||
'order' => array(
|
||||
'type' => 'uc_order',
|
||||
'label' => t('Order'),
|
||||
),
|
||||
'shipment' => array(
|
||||
'type' => 'uc_shipment',
|
||||
'label' => t('Shipment'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $events;
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Token hooks for the uc_shipping module.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function uc_shipping_token_info() {
|
||||
$tokens = array();
|
||||
|
||||
$tokens['tracking-number'] = array(
|
||||
'name' => t('Shipment tracking number(s)'),
|
||||
'description' => t('Tracking number(s) (if applicable) for product shipments.'),
|
||||
);
|
||||
|
||||
return array(
|
||||
'tokens' => array('uc_order' => $tokens),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function uc_shipping_tokens($type, $tokens, $data = array(), $options = array()) {
|
||||
$replacements = array();
|
||||
$sanitize = !empty($options['sanitize']);
|
||||
|
||||
if ($type == 'uc_order' && !empty($data['uc_order'])) {
|
||||
$order = $data['uc_order'];
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'tracking-number':
|
||||
$result = db_query('SELECT tracking_number FROM {uc_shipments} WHERE order_id = :order_id', array(':order_id' => $order->order_id));
|
||||
$tracking_numbers = array();
|
||||
foreach ($result as $record) {
|
||||
if ((isset($record->tracking_number)) && (!empty($record->tracking_number))) {
|
||||
$tracking_numbers[] = $record->tracking_number;
|
||||
}
|
||||
}
|
||||
$tracking = implode(', ', $tracking_numbers);
|
||||
$replacements[$original] = $sanitize ? check_plain($tracking) : $tracking;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
@@ -0,0 +1,675 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Views hooks for Ubercart shipping.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function uc_shipping_views_data() {
|
||||
$data['uc_shipments']['table']['group'] = t('Shipment');
|
||||
|
||||
// Allow base tables of shipments.
|
||||
$data['uc_shipments']['table']['base'] = array(
|
||||
'field' => 'sid', // This is the identifier field for the view.
|
||||
'title' => t('Ubercart shipments'),
|
||||
'help' => t('Ubercart shipments contain shipping information for orders and can be related to orders and packages.'),
|
||||
);
|
||||
|
||||
// Shipment relationship for orders.
|
||||
$data['uc_orders']['shipments'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Shipments'),
|
||||
'help' => t('Relate shipments to an order. This relationship will create one record for each shipment.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_shipments',
|
||||
'base field' => 'order_id',
|
||||
'relationship field' => 'order_id',
|
||||
'label' => t('shipment'),
|
||||
),
|
||||
);
|
||||
|
||||
// Order relationship for shipments.
|
||||
$data['uc_shipments']['order'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Order'),
|
||||
'help' => t('Relate an order to a shipment. Use this relationship to get order information for a shipment.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_orders',
|
||||
'base field' => 'order_id',
|
||||
'relationship field' => 'order_id',
|
||||
'label' => t('order'),
|
||||
),
|
||||
);
|
||||
|
||||
// Expose packages to their shipments as a relationship.
|
||||
$data['uc_shipments']['packages'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Packages'),
|
||||
'help' => t('Relate packages to a shipment. This relationship will create one record for each shipped package.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_packages',
|
||||
'base field' => 'sid',
|
||||
'relationship field' => 'sid',
|
||||
'label' => t('package'),
|
||||
),
|
||||
);
|
||||
|
||||
// Shipment ID field.
|
||||
$data['uc_shipments']['sid'] = array(
|
||||
'title' => t('Shipment ID'),
|
||||
'help' => t('The shipment ID.'),
|
||||
'field' => array(
|
||||
'handler' => 'uc_shipping_handler_field_shipment_id',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
'name field' => 'title',
|
||||
'numeric' => TRUE,
|
||||
'validate type' => 'sid',
|
||||
),
|
||||
);
|
||||
|
||||
// Order ID field.
|
||||
$data['uc_shipments']['order_id'] = array(
|
||||
'title' => t('Order ID'),
|
||||
'help' => t('The order ID.'),
|
||||
'field' => array(
|
||||
'handler' => 'uc_order_handler_field_order_id',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
'name field' => 'title',
|
||||
'numeric' => TRUE,
|
||||
'validate type' => 'order_id',
|
||||
),
|
||||
);
|
||||
|
||||
// Carrier field.
|
||||
$data['uc_shipments']['carrier'] = array(
|
||||
'title' => t('Carrier'),
|
||||
'help' => t('The company making the delivery.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
|
||||
// Shipment transaction ID field.
|
||||
$data['uc_shipments']['transaction_id'] = array(
|
||||
'title' => t('Transaction ID'),
|
||||
'help' => t("The carrier's shipment identifier."),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_string',
|
||||
),
|
||||
);
|
||||
|
||||
// Shipment tracking number field.
|
||||
$data['uc_shipments']['tracking_number'] = array(
|
||||
'title' => t('Tracking number'),
|
||||
'help' => t('The number used by the carrier to locate the shipment while it is in transit.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_string',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_shipments']['ship_date'] = array(
|
||||
'title' => t('Ship date'),
|
||||
'help' => t('The date when the shipment left the origin address.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_shipments']['expected_delivery'] = array(
|
||||
'title' => t('Expected delivery date'),
|
||||
'help' => t('The expected date of delivery.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_shipments']['changed'] = array(
|
||||
'title' => t('Last modified'),
|
||||
'help' => t('The time the shipment was last modified.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_shipments']['cost'] = array(
|
||||
'title' => t('Cost'),
|
||||
'help' => t('The cost of the shipment.'),
|
||||
'field' => array(
|
||||
'handler' => 'uc_order_handler_field_money_amount',
|
||||
'click sortable' => TRUE,
|
||||
'float' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
);
|
||||
|
||||
$addresses = array(
|
||||
'o' => t('Origin address'),
|
||||
'd' => t('Delivery address'),
|
||||
);
|
||||
|
||||
$fields = array(
|
||||
'first_name' => t('First name'),
|
||||
'last_name' => t('Last name'),
|
||||
'company' => t('Company'),
|
||||
'street1' => t('Street address 1'),
|
||||
'street2' => t('Street address 2'),
|
||||
'city' => t('City'),
|
||||
'postal_code' => t('Postal code'),
|
||||
);
|
||||
|
||||
foreach ($addresses as $prefix => $address) {
|
||||
$group = t('Shipment') . ': ' . $address;
|
||||
|
||||
foreach ($fields as $field => $label) {
|
||||
$data['uc_shipments'][$prefix . '_' . $field] = array(
|
||||
'group' => $group,
|
||||
'title' => $label,
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => drupal_strtolower($label), '!address' => drupal_strtolower($address))),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
}
|
||||
// uc_order is required by shipping module so is safe to use uc_order handler.
|
||||
$data['uc_shipments'][$prefix . '_full_name'] = array(
|
||||
'group' => $group,
|
||||
'title' => t('Full name'),
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => t('full name'), '!address' => drupal_strtolower($address))),
|
||||
'field' => array(
|
||||
'additional fields' => array(
|
||||
$prefix . '_first_name',
|
||||
$prefix . '_last_name'
|
||||
),
|
||||
'handler' => 'uc_order_handler_field_order_fullname',
|
||||
'prefix' => $prefix,
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_shipments_' . $prefix . '_countries']['table']['group'] = $group;
|
||||
$data['uc_shipments_' . $prefix . '_countries']['table']['join']['uc_shipments'] = array(
|
||||
'table' => 'uc_countries',
|
||||
'left_field' => $prefix . '_country',
|
||||
'field' => 'country_id',
|
||||
);
|
||||
$data['uc_shipments_' . $prefix . '_countries']['country_id'] = array(
|
||||
'title' => t('ISO country code (numeric)'),
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => t('numeric ISO country code'), '!address' => drupal_strtolower($address))),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
'name field' => 'country_iso_code_2',
|
||||
'numeric' => TRUE,
|
||||
'validate type' => 'country_id',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
);
|
||||
$data['uc_shipments_' . $prefix . '_countries']['country_name'] = array(
|
||||
'title' => t('Country'),
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => t('country name'), '!address' => drupal_strtolower($address))),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
$data['uc_shipments_' . $prefix . '_countries']['country_iso_code_2'] = array(
|
||||
'title' => t('ISO country code (2 characters)'),
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => t('ISO country code'), '!address' => drupal_strtolower($address))),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
$data['uc_shipments_' . $prefix . '_countries']['country_iso_code_3'] = array(
|
||||
'title' => t('ISO country code (3 characters)'),
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => t('ISO country code'), '!address' => drupal_strtolower($address))),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_shipments_' . $prefix . '_zones']['table']['group'] = $group;
|
||||
$data['uc_shipments_' . $prefix . '_zones']['table']['join']['uc_shipments'] = array(
|
||||
'table' => 'uc_zones',
|
||||
'left_field' => $prefix . '_zone',
|
||||
'field' => 'zone_id',
|
||||
);
|
||||
$data['uc_shipments_' . $prefix . '_zones']['zone_name'] = array(
|
||||
'title' => t('State/Province'),
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => t('state or province'), '!address' => drupal_strtolower($address))),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
$data['uc_shipments_' . $prefix . '_zones']['zone_code'] = array(
|
||||
'title' => t('State/Province code'),
|
||||
'help' => t('The !field of the !address of the shipment.', array('!field' => t('state or province code'), '!address' => drupal_strtolower($address))),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Expose packages.
|
||||
$data['uc_packages']['table']['group'] = t('Package');
|
||||
|
||||
// Allow base tables of packages.
|
||||
$data['uc_packages']['table']['base'] = array(
|
||||
'field' => 'sid', // This is the identifier field for the view.
|
||||
'title' => t('Ubercart packages'),
|
||||
'help' => t('Ubercart packages contain physical characteristics, tracking, and label information. They can be related to their orders, shipments, and contents.'),
|
||||
);
|
||||
|
||||
// Expose packages to their order as a relationship.
|
||||
// Packages can exists without a shipment so this relationship may be useful.
|
||||
$data['uc_orders']['packages'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Packages'),
|
||||
'help' => t('Relate packages to an order. This relationship will create one record for each package of an order.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_packages',
|
||||
'base field' => 'order_id',
|
||||
'relationship field' => 'order_id',
|
||||
'label' => t('package'),
|
||||
),
|
||||
);
|
||||
|
||||
// Order relationship for packages.
|
||||
$data['uc_packages']['order'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Order'),
|
||||
'help' => t('Relate an order to a package. Use this relationship to get order information for a package.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_orders',
|
||||
'base field' => 'order_id',
|
||||
'relationship field' => 'order_id',
|
||||
'label' => t('order'),
|
||||
),
|
||||
);
|
||||
|
||||
// Expose packaged products to their package as a relationship.
|
||||
$data['uc_packages']['packaged_products'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Products'),
|
||||
'help' => t('Relate packaged products to a package. This relationship will create one record for each packaged product.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_packaged_products',
|
||||
'base field' => 'package_id',
|
||||
'relationship field' => 'package_id',
|
||||
'label' => t('product'),
|
||||
),
|
||||
);
|
||||
|
||||
// Expose shipments to their packages as a relationship.
|
||||
$data['uc_packages']['shipment'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Shipment'),
|
||||
'help' => t('Relate shipment to package. Use this relationship to get shipping information for the package. Note that this relationship might not exist.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_shipments',
|
||||
'base field' => 'sid',
|
||||
'relationship field' => 'sid',
|
||||
'label' => t('shipment'),
|
||||
),
|
||||
);
|
||||
|
||||
// Package ID field.
|
||||
// We don't redirect to the shipment page because a package can exist without a shipment.
|
||||
$data['uc_packages']['package_id'] = array(
|
||||
'title' => t('Package ID'),
|
||||
'help' => t('The package ID.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
'name field' => 'title',
|
||||
'numeric' => TRUE,
|
||||
'validate type' => 'sid',
|
||||
),
|
||||
);
|
||||
|
||||
// Shipment ID field.
|
||||
$data['uc_packages']['sid'] = array(
|
||||
'title' => t('Shipment ID'),
|
||||
'help' => t('The shipment ID.'),
|
||||
'field' => array(
|
||||
'handler' => 'uc_shipping_handler_field_shipment_id',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
'name field' => 'title',
|
||||
'numeric' => TRUE,
|
||||
'validate type' => 'sid',
|
||||
),
|
||||
);
|
||||
|
||||
// Order ID field.
|
||||
$data['uc_packages']['order_id'] = array(
|
||||
'title' => t('Order ID'),
|
||||
'help' => t('The order ID.'),
|
||||
'field' => array(
|
||||
'handler' => 'uc_order_handler_field_order_id',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
'name field' => 'title',
|
||||
'numeric' => TRUE,
|
||||
'validate type' => 'order_id',
|
||||
),
|
||||
);
|
||||
|
||||
// Shipment type field.
|
||||
$data['uc_packages']['shipping_type'] = array(
|
||||
'title' => t('Shipment type'),
|
||||
'help' => t('The basic type of shipment, e.g.: small package, freight, etc.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
|
||||
// Package type.
|
||||
$data['uc_packages']['pkg_type'] = array(
|
||||
'title' => t('Package type'),
|
||||
'help' => t('The type of packaging.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
);
|
||||
|
||||
// Package value.
|
||||
$data['uc_packages']['value'] = array(
|
||||
'title' => t('Value'),
|
||||
'help' => t('The monetary value of the package contents.'),
|
||||
'field' => array(
|
||||
'handler' => 'uc_order_handler_field_money_amount',
|
||||
'click sortable' => TRUE,
|
||||
'float' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
);
|
||||
|
||||
// Package tracking number field.
|
||||
$data['uc_packages']['tracking_number'] = array(
|
||||
'title' => t('Tracking number'),
|
||||
'help' => t('The number used by the carrier to locate the package while it is in transit.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_string',
|
||||
),
|
||||
);
|
||||
|
||||
// Package length field.
|
||||
$data['uc_packages']['length'] = array(
|
||||
'title' => t('Length'),
|
||||
'help' => t('The physical length.'),
|
||||
'field' => array(
|
||||
'additional fields' => array(
|
||||
'field' => 'length_units',
|
||||
),
|
||||
'handler' => 'uc_product_handler_field_length',
|
||||
'click sortable' => TRUE,
|
||||
'float' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_float',
|
||||
),
|
||||
);
|
||||
|
||||
// Package width field.
|
||||
$data['uc_packages']['width'] = array(
|
||||
'title' => t('Width'),
|
||||
'help' => t('The physical width.'),
|
||||
'field' => array(
|
||||
'additional fields' => array(
|
||||
'field' => 'length_units',
|
||||
),
|
||||
'handler' => 'uc_product_handler_field_length',
|
||||
'click sortable' => TRUE,
|
||||
'float' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_float',
|
||||
),
|
||||
);
|
||||
|
||||
// Package height field.
|
||||
$data['uc_packages']['height'] = array(
|
||||
'title' => t('Height'),
|
||||
'help' => t('The physical height.'),
|
||||
'field' => array(
|
||||
'additional fields' => array(
|
||||
'field' => 'length_units',
|
||||
),
|
||||
'handler' => 'uc_product_handler_field_length',
|
||||
'click sortable' => TRUE,
|
||||
'float' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_float',
|
||||
),
|
||||
);
|
||||
|
||||
// Package weight field.
|
||||
$data['uc_packages']['weight'] = array(
|
||||
'title' => t('Weight'),
|
||||
'help' => t('The physical weight of package.'),
|
||||
'field' => array(
|
||||
'additional fields' => array(
|
||||
'package_id',
|
||||
),
|
||||
'handler' => 'uc_shipping_handler_field_package_weight',
|
||||
'click sortable' => FALSE,
|
||||
'float' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
// Expose packaged products.
|
||||
$data['uc_packaged_products']['table']['group'] = t('Package: Product');
|
||||
|
||||
// Expose packaged products to the ordered product as a relationship.
|
||||
// By using a relation and not expose fields directly we make sure that
|
||||
// when ordered products will be fieldable entities all their custom fields,
|
||||
// the one not stored in the schema, will get loaded.
|
||||
$data['uc_packaged_products']['uc_order_products'] = array(
|
||||
'relationship' => array(
|
||||
'title' => t('Ordered product'),
|
||||
'help' => t('Relate packaged product to the ordered product.'),
|
||||
'handler' => 'views_handler_relationship',
|
||||
'base' => 'uc_order_products',
|
||||
'base field' => 'order_product_id',
|
||||
'relationship field' => 'order_product_id',
|
||||
'label' => t('ordered product'),
|
||||
),
|
||||
);
|
||||
|
||||
// Packaged quantity field.
|
||||
$data['uc_packaged_products']['qty'] = array(
|
||||
'title' => t('Quantity'),
|
||||
'help' => t('The quantity packaged.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Total package weight field handler.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Field handler: displays the weight of the package.
|
||||
*
|
||||
* We cannot use a subquery because there is no way to make sure that all products
|
||||
* in packages have the same weight unit.
|
||||
*/
|
||||
class uc_shipping_handler_field_package_weight extends uc_product_handler_field_weight {
|
||||
/**
|
||||
* Overrides views_handler::use_group_by().
|
||||
*
|
||||
* Disables aggregation for this field.
|
||||
*/
|
||||
function use_group_by() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides uc_product_handler_field_weight::query().
|
||||
*/
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
$this->add_additional_fields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides uc_product_handler_field_weight::render().
|
||||
*/
|
||||
function render($values) {
|
||||
$package = uc_shipping_package_load($values->{$this->aliases['package_id']});
|
||||
|
||||
if ($this->options['format'] == 'numeric') {
|
||||
return $package->weight;
|
||||
}
|
||||
|
||||
if ($this->options['format'] == 'uc_weight') {
|
||||
return uc_weight_format($package->weight, $package->weight_units);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Shipment ID field handler.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Field handler: simple renderer that links to the shipment page.
|
||||
*/
|
||||
class uc_shipping_handler_field_shipment_id extends views_handler_field {
|
||||
|
||||
/**
|
||||
* Override init function to provide generic option to link to shipment.
|
||||
*/
|
||||
function init(&$view, &$data) {
|
||||
parent::init($view, $data);
|
||||
if (!empty($this->options['link_to_shipment'])) {
|
||||
$this->additional_fields['order_id'] = array('table' => $this->table_alias, 'field' => 'order_id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides views_handler::option_definition().
|
||||
*/
|
||||
function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options['link_to_shipment'] = array('default' => FALSE);
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides views_handler::options_form().
|
||||
*
|
||||
* Provides link to shipment administration page.
|
||||
*/
|
||||
function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
$form['link_to_shipment'] = array(
|
||||
'#title' => t('Link this field to the shipment page'),
|
||||
'#description' => t('This will override any other link you have set.'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => !empty($this->options['link_to_shipment']),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders whatever the data is as a link to the order.
|
||||
*
|
||||
* Data should be made XSS safe prior to calling this function.
|
||||
*/
|
||||
function render_link($data, $values) {
|
||||
if (!empty($this->options['link_to_shipment'])) {
|
||||
$this->options['alter']['make_link'] = FALSE;
|
||||
|
||||
if (user_access('fulfill orders')) {
|
||||
$path = 'admin/store/orders/' . $this->get_value($values, 'order_id') . '/shipments/' . $values->{$this->field_alias};
|
||||
}
|
||||
else {
|
||||
$path = FALSE;
|
||||
}
|
||||
|
||||
if ($path && $data !== NULL && $data !== '') {
|
||||
$this->options['alter']['make_link'] = TRUE;
|
||||
$this->options['alter']['path'] = $path;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides views_handler_field::render().
|
||||
*/
|
||||
function render($values) {
|
||||
return $this->render_link(check_plain($values->{$this->field_alias}), $values);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* UPS administration menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* UPS Online Tool settings.
|
||||
*
|
||||
* Records UPS account information necessary to use the service. Allows testing
|
||||
* or production mode. Configures which UPS services are quoted to customers.
|
||||
*
|
||||
* @see uc_ups_admin_settings_validate()
|
||||
* @see uc_ups_admin_settings_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_ups_admin_settings($form, &$form_state) {
|
||||
|
||||
// Put fieldsets into vertical tabs
|
||||
$form['ups-settings'] = array(
|
||||
'#type' => 'vertical_tabs',
|
||||
'#attached' => array(
|
||||
'js' => array(
|
||||
'vertical-tabs' => drupal_get_path('module', 'uc_ups') . '/uc_ups.admin.js',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Container for credential forms
|
||||
$form['uc_ups_credentials'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Credentials'),
|
||||
'#description' => t('Account number and authorization information.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'ups-settings',
|
||||
);
|
||||
|
||||
$form['uc_ups_credentials']['uc_ups_access_license'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('UPS OnLine Tools XML Access Key'),
|
||||
'#default_value' => variable_get('uc_ups_access_license', ''),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['uc_ups_credentials']['uc_ups_shipper_number'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('UPS Shipper #'),
|
||||
'#description' => t('The 6-character string identifying your UPS account as a shipper.'),
|
||||
'#default_value' => variable_get('uc_ups_shipper_number', ''),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['uc_ups_credentials']['uc_ups_user_id'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('UPS.com user ID'),
|
||||
'#default_value' => variable_get('uc_ups_user_id', ''),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
$form['uc_ups_credentials']['uc_ups_password'] = array(
|
||||
'#type' => 'password',
|
||||
'#title' => t('Password'),
|
||||
'#default_value' => variable_get('uc_ups_password', ''),
|
||||
);
|
||||
$form['uc_ups_credentials']['uc_ups_connection_address'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Server mode'),
|
||||
'#description' => t('Use the Testing server while developing and configuring your site. Switch to the Production server only after you have demonstrated that transactions on the Testing server are working and you are ready to go live.'),
|
||||
'#options' => array('https://wwwcie.ups.com/ups.app/xml/' => t('Testing'),
|
||||
'https://onlinetools.ups.com/ups.app/xml/' => t('Production'),
|
||||
),
|
||||
'#default_value' => variable_get('uc_ups_connection_address', 'https://wwwcie.ups.com/ups.app/xml/'),
|
||||
);
|
||||
|
||||
$form['services'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Service options'),
|
||||
'#description' => t('Set the conditions that will return a UPS quote.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'ups-settings',
|
||||
);
|
||||
|
||||
$form['services']['uc_ups_services'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('UPS services'),
|
||||
'#default_value' => variable_get('uc_ups_services', _uc_ups_service_list()),
|
||||
'#options' => _uc_ups_service_list(),
|
||||
'#description' => t('Select the UPS services that are available to customers.'),
|
||||
);
|
||||
|
||||
// Container for quote options
|
||||
$form['uc_ups_quote_options'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Quote options'),
|
||||
'#description' => t('Preferences that affect computation of quote.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'ups-settings',
|
||||
);
|
||||
|
||||
$form['uc_ups_quote_options']['uc_ups_all_in_one'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Product packages'),
|
||||
'#default_value' => variable_get('uc_ups_all_in_one', 1),
|
||||
'#options' => array(
|
||||
0 => t('Each product in its own package'),
|
||||
1 => t('All products in one package'),
|
||||
),
|
||||
'#description' => t('Indicate whether each product is quoted as shipping separately or all in one package. Orders with one kind of product will still use the package quantity to determine the number of packages needed, however.'),
|
||||
);
|
||||
|
||||
// Form to select package types
|
||||
$form['uc_ups_quote_options']['uc_ups_pkg_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Default Package Type'),
|
||||
'#default_value' => variable_get('uc_ups_pkg_type', _uc_ups_pkg_types()),
|
||||
'#options' => _uc_ups_pkg_types(),
|
||||
'#description' => t('Type of packaging to be used. May be overridden on a per-product basis via the product node edit form.'),
|
||||
);
|
||||
$form['uc_ups_quote_options']['uc_ups_classification'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('UPS Customer classification'),
|
||||
'#options' => array(
|
||||
'01' => t('Wholesale'),
|
||||
'03' => t('Occasional'),
|
||||
'04' => t('Retail'),
|
||||
),
|
||||
'#default_value' => variable_get('uc_ups_classification', '04'),
|
||||
'#description' => t('The kind of customer you are to UPS. For daily pickups the default is wholesale; for customer counter pickups the default is retail; for other pickups the default is occasional.'),
|
||||
);
|
||||
|
||||
$form['uc_ups_quote_options']['uc_ups_negotiated_rates'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Negotiated rates'),
|
||||
'#default_value' => variable_get('uc_ups_negotiated_rates', 0),
|
||||
'#options' => array(1 => t('Yes'), 0 => t('No')),
|
||||
'#description' => t('Is your UPS account receiving negotiated rates on shipments?'),
|
||||
);
|
||||
|
||||
// Form to select pickup type
|
||||
$form['uc_ups_quote_options']['uc_ups_pickup_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Pickup type'),
|
||||
'#options' => array(
|
||||
'01' => 'Daily Pickup',
|
||||
'03' => 'Customer Counter',
|
||||
'06' => 'One Time Pickup',
|
||||
'07' => 'On Call Air',
|
||||
'11' => 'Suggested Retail Rates',
|
||||
'19' => 'Letter Center',
|
||||
'20' => 'Air Service Center',
|
||||
),
|
||||
'#default_value' => variable_get('uc_ups_pickup_type', '01'),
|
||||
);
|
||||
|
||||
$form['uc_ups_quote_options']['uc_ups_residential_quotes'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Assume UPS shipping quotes will be delivered to'),
|
||||
'#default_value' => variable_get('uc_ups_residential_quotes', 0),
|
||||
'#options' => array(
|
||||
0 => t('Business locations'),
|
||||
1 => t('Residential locations (extra fees)'),
|
||||
),
|
||||
);
|
||||
|
||||
$form['uc_ups_quote_options']['uc_ups_unit_system'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('System of measurement'),
|
||||
'#default_value' => variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in')),
|
||||
'#options' => array(
|
||||
'in' => t('British'),
|
||||
'cm' => t('Metric'),
|
||||
),
|
||||
'#description' => t('Choose the standard system of measurement for your country.'),
|
||||
);
|
||||
|
||||
$form['uc_ups_quote_options']['uc_ups_insurance'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Package insurance'),
|
||||
'#default_value' => variable_get('uc_ups_insurance', TRUE),
|
||||
'#description' => t('When enabled, the quotes presented to the customer will include the cost of insurance for the full sales price of all products in the order.'),
|
||||
);
|
||||
|
||||
// Container for markup forms
|
||||
$form['uc_ups_markups'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Markups'),
|
||||
'#description' => t('Modifiers to the shipping weight and quoted rate.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'ups-settings',
|
||||
);
|
||||
|
||||
// Form to select type of rate markup
|
||||
$form['uc_ups_markups']['uc_ups_rate_markup_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Rate markup type'),
|
||||
'#default_value' => variable_get('uc_ups_rate_markup_type', 'percentage'),
|
||||
'#options' => array(
|
||||
'percentage' => t('Percentage (%)'),
|
||||
'multiplier' => t('Multiplier (×)'),
|
||||
'currency' => t('Addition (!currency)', array('!currency' => variable_get('uc_currency_sign', '$'))),
|
||||
),
|
||||
);
|
||||
|
||||
// Form to select rate markup amount
|
||||
$form['uc_ups_markups']['uc_ups_rate_markup'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Shipping rate markup'),
|
||||
'#default_value' => variable_get('uc_ups_rate_markup', '0'),
|
||||
'#description' => t('Markup shipping rate quote by currency amount, percentage, or multiplier.'),
|
||||
);
|
||||
|
||||
// Form to select type of weight markup
|
||||
$form['uc_ups_markups']['uc_ups_weight_markup_type'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Weight markup type'),
|
||||
'#default_value' => variable_get('uc_ups_weight_markup_type', 'percentage'),
|
||||
'#options' => array(
|
||||
'percentage' => t('Percentage (%)'),
|
||||
'multiplier' => t('Multiplier (×)'),
|
||||
'mass' => t('Addition (!mass)', array('!mass' => '#')),
|
||||
),
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
|
||||
// Form to select weight markup amount
|
||||
$form['uc_ups_markups']['uc_ups_weight_markup'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Shipping weight markup'),
|
||||
'#default_value' => variable_get('uc_ups_weight_markup', '0'),
|
||||
'#description' => t('Markup UPS shipping weight on a per-package basis before quote, by weight amount, percentage, or multiplier.'),
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
|
||||
// Container for label printing
|
||||
$form['uc_ups_labels'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Label Printing'),
|
||||
'#description' => t('Preferences for UPS Shipping Label Printing. Additional permissions from UPS are required to use this feature.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#group' => 'ups-settings',
|
||||
);
|
||||
|
||||
$period = drupal_map_assoc(array(86400, 302400, 604800, 1209600, 2419200, 0), 'format_interval');
|
||||
$period[0] = t('Forever');
|
||||
|
||||
// Form to select how long labels stay on server
|
||||
$form['uc_ups_labels']['uc_ups_label_lifetime'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Label lifetime'),
|
||||
'#default_value' => variable_get('uc_ups_label_lifetime', 0),
|
||||
'#options' => $period,
|
||||
'#description' => t('Controls how long labels are stored on the server before being automatically deleted. Cron must be enabled for automatic deletion. Default is never delete the labels, keep them forever.'),
|
||||
);
|
||||
|
||||
// Taken from system_settings_form(). Only, don't use its submit handler.
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save configuration'),
|
||||
);
|
||||
$form['actions']['cancel'] = array(
|
||||
'#markup' => l(t('Cancel'), 'admin/store/settings/quotes'),
|
||||
);
|
||||
|
||||
if (!empty($_POST) && form_get_errors()) {
|
||||
drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
|
||||
}
|
||||
if (!isset($form['#theme'])) {
|
||||
$form['#theme'] = 'system_settings_form';
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation handler for uc_ups_admin_settings.
|
||||
*
|
||||
* Requires password only if it hasn't been set.
|
||||
*
|
||||
* @see uc_ups_admin_settings()
|
||||
* @see uc_ups_admin_settings_submit()
|
||||
*/
|
||||
function uc_ups_admin_settings_validate($form, &$form_state) {
|
||||
$old_password = variable_get('uc_ups_password', '');
|
||||
if (!$form_state['values']['uc_ups_password']) {
|
||||
if ($old_password) {
|
||||
form_set_value($form['uc_ups_credentials']['uc_ups_password'], $old_password, $form_state);
|
||||
}
|
||||
else {
|
||||
form_set_error('uc_ups_password', t('Password field is required.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_numeric($form_state['values']['uc_ups_rate_markup'])) {
|
||||
form_set_error('uc_ups_rate_markup', t('Rate markup must be a numeric value.'));
|
||||
}
|
||||
if (!is_numeric($form_state['values']['uc_ups_weight_markup'])) {
|
||||
form_set_error('uc_ups_weight_markup', t('Weight markup must be a numeric value.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for uc_ups_admin_settings().
|
||||
*
|
||||
* Emulates system_settings_form_submit(), but only on a subset of the
|
||||
* form values.
|
||||
*
|
||||
* @see uc_ups_admin_settings()
|
||||
* @see uc_ups_admin_settings_validate()
|
||||
*/
|
||||
function uc_ups_admin_settings_submit($form, &$form_state) {
|
||||
$fields = array(
|
||||
'uc_ups_access_license',
|
||||
'uc_ups_shipper_number',
|
||||
'uc_ups_user_id',
|
||||
'uc_ups_password',
|
||||
'uc_ups_connection_address',
|
||||
'uc_ups_services',
|
||||
'uc_ups_pickup_type',
|
||||
'uc_ups_pkg_type',
|
||||
'uc_ups_classification',
|
||||
'uc_ups_negotiated_rates',
|
||||
'uc_ups_residential_quotes',
|
||||
'uc_ups_rate_markup_type',
|
||||
'uc_ups_rate_markup',
|
||||
'uc_ups_weight_markup_type',
|
||||
'uc_ups_weight_markup',
|
||||
'uc_ups_label_lifetime',
|
||||
'uc_ups_all_in_one',
|
||||
'uc_ups_unit_system',
|
||||
'uc_ups_insurance',
|
||||
);
|
||||
|
||||
foreach ($fields as $key) {
|
||||
$value = $form_state['values'][$key];
|
||||
|
||||
if (is_array($value) && isset($form_state['values']['array_filter'])) {
|
||||
$value = array_keys(array_filter($value));
|
||||
}
|
||||
variable_set($key, $value);
|
||||
}
|
||||
|
||||
drupal_set_message(t('The configuration options have been saved.'));
|
||||
|
||||
cache_clear_all();
|
||||
drupal_theme_rebuild();
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file
|
||||
* Utility functions to display settings summaries on vertical tabs.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.upsAdminFieldsetSummaries = {
|
||||
attach: function (context) {
|
||||
$('fieldset#edit-uc-ups-credentials', context).drupalSetSummary(function(context) {
|
||||
var server = $('#edit-uc-ups-connection-address :selected', context).text().toLowerCase();
|
||||
return Drupal.t('Using UPS @role server', { '@role': server });
|
||||
});
|
||||
|
||||
$('fieldset#edit-uc-ups-markups', context).drupalSetSummary(function(context) {
|
||||
return Drupal.t('Rate markup') + ': '
|
||||
+ $('#edit-uc-ups-rate-markup', context).val() + ' '
|
||||
+ $('#edit-uc-ups-rate-markup-type', context).val() + '<br />'
|
||||
+ Drupal.t('Weight markup') + ': '
|
||||
+ $('#edit-uc-ups-weight-markup', context).val() + ' '
|
||||
+ $('#edit-uc-ups-weight-markup-type', context).val();
|
||||
});
|
||||
|
||||
$('fieldset#edit-uc-ups-quote-options', context).drupalSetSummary(function(context) {
|
||||
if ($('#edit-uc-ups-insurance').is(':checked')) {
|
||||
return Drupal.t('Packages are insured');
|
||||
}
|
||||
else {
|
||||
return Drupal.t('Packages are not insured');
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
@@ -0,0 +1,14 @@
|
||||
name = UPS
|
||||
description = Integrates UPS Rates and Services Selection and Shipping Online Tools.
|
||||
dependencies[] = uc_quote
|
||||
package = Ubercart - fulfillment
|
||||
core = 7.x
|
||||
|
||||
configure = admin/store/settings/quotes/settings/ups
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_ups module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_ups_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_ups_products'] = array(
|
||||
'description' => 'Stores product information for UPS shipping quotes.',
|
||||
'fields' => array(
|
||||
'vid' => array(
|
||||
'description' => 'The {uc_products}.vid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'nid' => array(
|
||||
'description' => 'The {uc_products}.nid.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'pkg_type' => array(
|
||||
'description' => 'The type of package in which the product will be shipped.',
|
||||
'type' => 'varchar',
|
||||
'length' => 2,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
),
|
||||
'primary key' => array('vid'),
|
||||
'foreign keys' => array(
|
||||
'uc_products' => array(
|
||||
'table' => 'uc_products',
|
||||
'columns' => array(
|
||||
'nid' => 'nid',
|
||||
'vid' => 'vid',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_ups_uninstall() {
|
||||
variable_del('uc_ups_access_license');
|
||||
variable_del('uc_ups_shipper_number');
|
||||
variable_del('uc_ups_user_id');
|
||||
variable_del('uc_ups_password');
|
||||
variable_del('uc_ups_connection_address');
|
||||
variable_del('uc_ups_services');
|
||||
variable_del('uc_ups_pickup_type');
|
||||
variable_del('uc_ups_pkg_type');
|
||||
variable_del('uc_ups_classification');
|
||||
variable_del('uc_ups_negotiated_rates');
|
||||
variable_del('uc_ups_all_in_one');
|
||||
variable_del('uc_ups_unit_system');
|
||||
variable_del('uc_ups_insurance');
|
||||
variable_del('uc_ups_rate_markup');
|
||||
variable_del('uc_ups_rate_markup_type');
|
||||
variable_del('uc_ups_weight_markup');
|
||||
variable_del('uc_ups_weight_markup_type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Separates markup variables into rate_markup and weight_markup.
|
||||
*/
|
||||
function uc_ups_update_7300() {
|
||||
// Rename variables while preserving previous setting values
|
||||
variable_set('uc_ups_rate_markup', variable_get('uc_ups_markup', ''));
|
||||
variable_set('uc_ups_rate_markup_type', variable_get('uc_ups_markup_type', ''));
|
||||
|
||||
// Remove old variables
|
||||
variable_del('uc_ups_markup');
|
||||
variable_del('uc_ups_markup_type');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates UPS Production URL.
|
||||
*/
|
||||
function uc_ups_update_7301() {
|
||||
$current = variable_get('uc_ups_connection_address', '');
|
||||
|
||||
// If currently using production URL, update variable to new value.
|
||||
if ($current == 'https://www.ups.com/ups.app/xml/') {
|
||||
variable_set('uc_ups_connection_address',
|
||||
'https://onlinetools.ups.com/ups.app/xml/');
|
||||
}
|
||||
}
|