Add custom vendor catalog batch handler for CORE
capable of directly updating `vendorItems` etc. via CORE API
This commit is contained in:
		
							parent
							
								
									1be258246c
								
							
						
					
					
						commit
						d0bb4b105d
					
				
					 2 changed files with 149 additions and 0 deletions
				
			
		
							
								
								
									
										0
									
								
								rattail_corepos/batch/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								rattail_corepos/batch/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										149
									
								
								rattail_corepos/batch/vendorcatalog.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								rattail_corepos/batch/vendorcatalog.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,149 @@ | |||
| # -*- coding: utf-8; -*- | ||||
| ################################################################################ | ||||
| # | ||||
| #  Rattail -- Retail Software Framework | ||||
| #  Copyright © 2010-2021 Lance Edgar | ||||
| # | ||||
| #  This file is part of Rattail. | ||||
| # | ||||
| #  Rattail 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 3 of the License, or (at your option) any later | ||||
| #  version. | ||||
| # | ||||
| #  Rattail 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 | ||||
| #  Rattail.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| ################################################################################ | ||||
| """ | ||||
| Handler for Vendor Catalog batches | ||||
| """ | ||||
| 
 | ||||
| import decimal | ||||
| 
 | ||||
| from rattail.batch import vendorcatalog as base | ||||
| from rattail_corepos.corepos.api import make_corepos_api | ||||
| 
 | ||||
| 
 | ||||
| class VendorCatalogHandler(base.VendorCatalogHandler): | ||||
|     """ | ||||
|     Handler for vendor catalog batches. | ||||
|     """ | ||||
|     # default logic tries to update versions after execution, but since we just | ||||
|     # update CORE-POS via API there's no need | ||||
|     version_catchup_execute = None | ||||
| 
 | ||||
|     def add_row(self, batch, row): | ||||
| 
 | ||||
|         # CORE unit costs seem to be rounded to 3 decimal places | ||||
|         if row.unit_cost: | ||||
|             row.unit_cost = row.unit_cost.quantize(decimal.Decimal('0.123')) | ||||
| 
 | ||||
|         # CORE bases its cost off the unit instead of case.  so here we make | ||||
|         # sure the case cost is cleanly derived from unit cost. | ||||
|         if row.case_cost and row.unit_cost and row.case_size: | ||||
|             row.case_cost = row.unit_cost * row.case_size | ||||
| 
 | ||||
|         # okay now continue as normal | ||||
|         super(VendorCatalogHandler, self).add_row(batch, row) | ||||
| 
 | ||||
|     def describe_execution(self, batch, **kwargs): | ||||
|         return ("The `vendorItems` table in CORE-POS will be updated directly " | ||||
|                 "via API, for all rows indicating a change etc.  You may also " | ||||
|                 "update `products.cost` if desired.") | ||||
| 
 | ||||
|     def execute(self, batch, progress=None, **kwargs): | ||||
|         """ | ||||
|         Update CORE-POS etc. | ||||
|         """ | ||||
|         rows = [row for row in batch.active_rows() | ||||
|                 if row.status_code in (row.STATUS_NEW_COST, | ||||
|                                        row.STATUS_UPDATE_COST, | ||||
|                                        row.STATUS_CHANGE_VENDOR_ITEM_CODE, | ||||
|                                        row.STATUS_CHANGE_CASE_SIZE, | ||||
|                                        row.STATUS_CHANGE_COST)] | ||||
| 
 | ||||
|         if rows: | ||||
|             vendor_id = batch.vendor.corepos_id | ||||
|             if not vendor_id: | ||||
|                 raise ValueError("Batch vendor does not have valid CORE-POS ID") | ||||
| 
 | ||||
|             self.api = make_corepos_api(self.config) | ||||
|             self.update_corepos(batch, rows, vendor_id, progress=progress, | ||||
|                                 # update_product_costs=kwargs.get('update_product_costs', False), | ||||
|             ) | ||||
| 
 | ||||
|         return True | ||||
| 
 | ||||
|     def update_corepos(self, batch, rows, vendor_id, progress=None, | ||||
|                        # TODO: this kwarg seems perhaps useful, but for now we | ||||
|                        # are auto-detecting when such an update is needed | ||||
|                        #update_product_costs=False, | ||||
|     ): | ||||
|         """ | ||||
|         Update the `vendorItems` table in CORE-POS (and maybe `products` too). | ||||
|         """ | ||||
|         def update(row, i): | ||||
|             # we may need this value in a couple places | ||||
|             unit_cost = float(row.unit_cost) | ||||
| 
 | ||||
|             # we will want to set default vendor for the product, if it does | ||||
|             # not yet have one | ||||
|             first_vendor = False | ||||
|             if not row.product.costs: | ||||
|                 first_vendor = True | ||||
|             # core_product = self.api.get_product(row.item_id) | ||||
|             # if not core_product['default_vendor_id']: | ||||
|             #     first_vendor = True | ||||
| 
 | ||||
|             # figure out if we are "updating the same, primary" cost record, | ||||
|             # b/c if so we will want to update product accordingly also.  this | ||||
|             # is always the case when this is the first vendor for product. | ||||
|             updating_primary = first_vendor | ||||
|             if not updating_primary: | ||||
|                 cost = row.product.cost | ||||
|                 if cost and cost is row.cost: | ||||
|                     updating_primary = True | ||||
|                 # core_items = self.api.get_vendor_items(upc=row.item_id) | ||||
|                 # if core_items: | ||||
|                 #     core_item = core_items[0] | ||||
|                 #     if core_item['sku'] == row.vendor_code: | ||||
|                 #         updating_primary = True | ||||
| 
 | ||||
|             # create or update the `vendorItems` record in CORE | ||||
|             self.api.set_vendor_item(sku=row.vendor_code, | ||||
|                                      vendorID=vendor_id, | ||||
|                                      upc=row.item_id, | ||||
|                                      brand=row.brand_name, | ||||
|                                      description=row.description, | ||||
|                                      size=row.size, | ||||
|                                      units=row.case_size, | ||||
|                                      cost=unit_cost, | ||||
|                                      # TODO: we (may) have vendor SRP, but pretty | ||||
|                                      # sure CORE has different plans for its `srp` | ||||
|                                      #srp=row.suggested_retail, | ||||
|             ) | ||||
| 
 | ||||
|             # TODO: CORE does not have the concept of a true "default" | ||||
|             # `vendorItems` record, but rather uses the `modified` timestamp | ||||
|             # for pseudo-default.  this means any given product may wind up | ||||
|             # with a new/different pseudo-default when the above operation | ||||
|             # completes.  in which case, perhaps we should *always* update | ||||
|             # `products.cost` accordingly (below)..?  er, still only if the | ||||
|             # product's `default_vendor_id` matches at least, i guess...  for | ||||
|             # now we are only doing so if it "obviously" needs it. | ||||
| 
 | ||||
|             # maybe also update `products` record in CORE | ||||
|             if updating_primary: | ||||
|                 kwargs = {'cost': unit_cost} | ||||
|                 if first_vendor: | ||||
|                     kwargs['default_vendor_id'] = vendor_id | ||||
|                 self.api.set_product(upc=row.item_id, **kwargs) | ||||
| 
 | ||||
|         self.progress_loop(update, rows, progress, | ||||
|                            message="Updating CORE-POS via API") | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar