Add feature to download Purchase data for import to CORE
This commit is contained in:
parent
fc102e6cc0
commit
1df59ff8ec
104
tailbone_corepos/templates/purchases/view.mako
Normal file
104
tailbone_corepos/templates/purchases/view.mako
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="tailbone:templates/purchases/view.mako" />
|
||||||
|
|
||||||
|
<%def name="object_helpers()">
|
||||||
|
${parent.object_helpers()}
|
||||||
|
% if master.has_perm('download_for_corepos'):
|
||||||
|
<div class="object-helper">
|
||||||
|
<h3>Integrations</h3>
|
||||||
|
<div class="object-helper-content">
|
||||||
|
<corepos-downloader></corepos-downloader>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_this_page_template()">
|
||||||
|
${parent.render_this_page_template()}
|
||||||
|
% if master.has_perm('download_for_corepos'):
|
||||||
|
<script type="text/x-template" id="corepos-downloader-template">
|
||||||
|
<div>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="fas fa-download"
|
||||||
|
@click="showDialog = true">
|
||||||
|
Download for CORE-POS
|
||||||
|
</b-button>
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="showDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Download Purchase for CORE-POS</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<p class="block">
|
||||||
|
You can download this Purchase as a data file, which can then
|
||||||
|
be imported as a new Purchase Order in CORE Office.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<div class="level" style="width: 100%;">
|
||||||
|
<div class="level-left">
|
||||||
|
<div class="level-item buttons">
|
||||||
|
<b-button @click="showDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
@click="beginDownload()"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="download">
|
||||||
|
Download for CORE-POS
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% if corepos_import_url:
|
||||||
|
<div class="level-right">
|
||||||
|
<div class="level-item buttons">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
tag="a" href="${corepos_import_url}" target="_blank"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="fas fa-external-link-alt">
|
||||||
|
Go to CORE Office
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="finalize_this_page_vars()">
|
||||||
|
${parent.finalize_this_page_vars()}
|
||||||
|
% if master.has_perm('download_for_corepos'):
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
let CoreposDownloader = {
|
||||||
|
template: '#corepos-downloader-template',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showDialog: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
beginDownload() {
|
||||||
|
location.href = '${url('{}.download_for_corepos'.format(route_prefix), uuid=instance.uuid)}'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.component('corepos-downloader', CoreposDownloader)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
134
tailbone_corepos/views/purchases.py
Normal file
134
tailbone_corepos/views/purchases.py
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
# -*- 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/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Purchase views
|
||||||
|
"""
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
|
||||||
|
from rattail.util import pretty_quantity
|
||||||
|
from rattail_corepos.config import core_office_url
|
||||||
|
|
||||||
|
from tailbone.views.purchases import core as base
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseView(base.PurchaseView):
|
||||||
|
"""
|
||||||
|
Expose some extra features per CORE-POS integration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def template_kwargs_view(self, **kwargs):
|
||||||
|
kwargs = super(PurchaseView, self).template_kwargs_view(**kwargs)
|
||||||
|
|
||||||
|
url = core_office_url(self.rattail_config)
|
||||||
|
if url:
|
||||||
|
url = '{}/purchasing/ImportPurchaseOrder.php'.format(url)
|
||||||
|
kwargs['corepos_import_url'] = url
|
||||||
|
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def download_for_corepos(self):
|
||||||
|
"""
|
||||||
|
View for downloading a Purchase data file for import to CORE-POS.
|
||||||
|
"""
|
||||||
|
purchase = self.get_instance()
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
tmpdir = app.make_temp_dir()
|
||||||
|
path = os.path.join(tmpdir, 'Purchase {}.csv'.format(purchase.id_str))
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'sku',
|
||||||
|
'cost_total',
|
||||||
|
'quantity_units',
|
||||||
|
'quantity_cases',
|
||||||
|
'units_per_case',
|
||||||
|
'unit_size',
|
||||||
|
'brand',
|
||||||
|
'description',
|
||||||
|
'upc_without_check',
|
||||||
|
'upc_with_check',
|
||||||
|
]
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
def collect(item, i):
|
||||||
|
upc = item.upc
|
||||||
|
if upc:
|
||||||
|
# TODO: i am still confused by what really is expected for the
|
||||||
|
# 'Unit Size' field here. CORE *behavior* seems to prefer what
|
||||||
|
# logically maps to Rattail `Product.unit_size` (e.g. 12) but
|
||||||
|
# the data type for CORE `PurchaseOrderItems.unitSize` is varchar
|
||||||
|
# and code comments imply e.g. '12 OZ' would also be expected.
|
||||||
|
# for now i am being "conservative" and mimicking CORE *behavior*.
|
||||||
|
unit_size = None
|
||||||
|
if item.product:
|
||||||
|
unit_size = pretty_quantity(item.product.unit_size)
|
||||||
|
rows.append({
|
||||||
|
'sku': item.vendor_code,
|
||||||
|
'cost_total': item.po_total,
|
||||||
|
'quantity_units': pretty_quantity(item.units_ordered),
|
||||||
|
'quantity_cases': pretty_quantity(item.cases_ordered),
|
||||||
|
'units_per_case': pretty_quantity(item.case_quantity),
|
||||||
|
'unit_size': unit_size,
|
||||||
|
'brand': item.brand_name,
|
||||||
|
'description': item.description,
|
||||||
|
'upc_without_check': str(upc)[:-1],
|
||||||
|
'upc_with_check': str(upc),
|
||||||
|
})
|
||||||
|
|
||||||
|
self.progress_loop(collect, purchase.items, None, # TODO
|
||||||
|
message="Converting data to CSV")
|
||||||
|
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
writer = csv.DictWriter(f, fields)
|
||||||
|
# note, the CORE importer does not really need or expect a header
|
||||||
|
# TODO: seems like it should? b/c would be more helpful for humans
|
||||||
|
#writer.writeheader()
|
||||||
|
writer.writerows(rows)
|
||||||
|
|
||||||
|
return self.file_response(path)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def defaults(cls, config):
|
||||||
|
cls._corepos_purchase_defaults(config)
|
||||||
|
cls._purchase_defaults(config)
|
||||||
|
cls._defaults(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _corepos_purchase_defaults(cls, config):
|
||||||
|
route_prefix = cls.get_route_prefix()
|
||||||
|
instance_url_prefix = cls.get_instance_url_prefix()
|
||||||
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
model_title = cls.get_model_title()
|
||||||
|
|
||||||
|
# download for core-pos
|
||||||
|
config.add_route('{}.download_for_corepos'.format(route_prefix), '{}/download-for-corepos'.format(instance_url_prefix))
|
||||||
|
config.add_view(cls, attr='download_for_corepos', route_name='{}.download_for_corepos'.format(route_prefix),
|
||||||
|
permission='{}.download_for_corepos'.format(permission_prefix))
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{}.download'.format(permission_prefix),
|
||||||
|
"Download {} for import to CORE-POS".format(model_title))
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
PurchaseView.defaults(config)
|
Loading…
Reference in a new issue