diff --git a/__init__.py b/__init__.py
index da03b23..ecec6c6 100644
--- a/__init__.py
+++ b/__init__.py
@@ -11,13 +11,13 @@ def register():
purchase.Purchase,
purchase.Line,
purchase.PurchaseAnalyticStart,
- purchase.PurchaseDetailedStart,
+ purchase.PurchasesDetailedStart,
module='purchase_co', type_='model')
Pool.register(
- purchase.PurchaseDetailed,
+ purchase.PurchasesDetailed,
purchase.PurchaseAnalytic,
module='purchase_co', type_='wizard')
Pool.register(
+ purchase.PurchasesDetailedReport,
purchase.PurchaseAnalyticReport,
- purchase.PurchaseDetailedReport,
module='purchase_co', type_='report')
diff --git a/analytic_report.ods b/analytic_report.fods
similarity index 100%
rename from analytic_report.ods
rename to analytic_report.fods
diff --git a/detailed_report.ods b/detailed_report.fods
similarity index 100%
rename from detailed_report.ods
rename to detailed_report.fods
diff --git a/purchase.xml b/purchase.xml
index 1162318..19cd96a 100644
--- a/purchase.xml
+++ b/purchase.xml
@@ -18,33 +18,21 @@ this repository contains the full copyright notices and license terms. -->
Purchase Analytic Report
- purchase_report.analytic.report
- purchase_report/analytic_report.ods
+ purchase_co.analytic.report
+ purchase_co/analytic_report.fods
- purchase_report.analytic.start
+ purchase_co.analytic.start
form
report_analytic_start_form
Wizard Purchase Analytic Report
- purchase_report.analytic
+ purchase_co.analytic
-
-
purchase.line
tree
@@ -84,28 +72,26 @@ this repository contains the full copyright notices and license terms. -->
-
- purchase.purchase_detailed.start
+
+ purchase.purchases_detailed.start
form
print_purchase_detailed_start_form
-
- Print Purchase Detailed
- purchase.purchase_detailed
+
+ Detailed Purchases
+ purchase.purchases_detailed
-
-
- Purchase Detailed
- purchase.purchase
- purchase.purchase_detailed.report
- purchase_co/purchase_detailed.fods
+
+ Purchases Detailed
+
+ purchase.purchases_detailed.report
+ purchase_co/purchases_detailed.fods
ods
- True
+ False
-
-
-
+
diff --git a/purchase_new.py b/purchase_new.py
new file mode 100644
index 0000000..e023308
--- /dev/null
+++ b/purchase_new.py
@@ -0,0 +1,353 @@
+# This file is part of Tryton. The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+from datetime import date, timedelta
+from decimal import Decimal
+from trytond.pyson import Eval
+from trytond.pool import PoolMeta, Pool
+from trytond.model import fields, ModelView
+from trytond.exceptions import UserError
+from trytond.transaction import Transaction
+from trytond.report import Report
+from trytond.wizard import Wizard, StateView, Button, StateReport
+
+
+class Configuration(metaclass=PoolMeta):
+ __name__ = 'purchase.configuration'
+ reference_required = fields.Boolean('Reference Required')
+ all_warehouse = fields.Boolean('All Warehouse', 'See quantity in all warehouse')
+
+
+class Purchase(metaclass=PoolMeta):
+ __name__ = 'purchase.purchase'
+
+ @fields.depends('party', 'invoice_party', 'payment_term', 'lines')
+ def on_change_party(self):
+ super(Purchase, self).on_change_party()
+ self.invoice_method = 'shipment'
+
+ def create_invoice(self):
+ invoice = super(Purchase, self).create_invoice()
+ if not invoice:
+ return
+ invoice.reference = self.reference
+ invoice.description = self.description
+ invoice.save()
+ return invoice
+
+ @classmethod
+ def process(cls, purchases):
+ for rec in purchases:
+ if not rec.reference:
+ raise UserError('El campo referencia es obligatorio!')
+ super(Purchase, cls).process(purchases)
+
+ @classmethod
+ def quote(cls, purchases):
+ pool = Pool()
+ Configuration = pool.get('purchase.configuration')
+ config = Configuration(1)
+ if config.reference_required:
+ cls.check_duplicated(purchases)
+ super(Purchase, cls).quote(purchases)
+
+ @classmethod
+ def check_duplicated(cls, purchases):
+ today = date.today()
+ target_date = today - timedelta(days=90)
+ for purchase in purchases:
+ duplicates = cls.search_read([
+ ('reference', '=', purchase.reference),
+ ('party', '=', purchase.party.id),
+ ('purchase_date', '>=', target_date),
+ ], fields_names=['reference'])
+ if len(duplicates) >= 2:
+ raise UserError('Al parecer esta compra esta duplicada!')
+
+
+class Line(metaclass=PoolMeta):
+ __name__ = 'purchase.line'
+
+ stock_quantity = fields.Function(fields.Float('Stock Quantity',
+ digits=(16, Eval('default_uom_digits', 2))),
+ 'on_change_with_stock_quantity')
+
+ @fields.depends('product', 'purchase',
+ '_parent_purchase.warehouse')
+ def on_change_with_stock_quantity(self, name=None):
+ if self.product:
+ context = {'stock_date_end': date.today()}
+ Location = Pool().get('stock.location')
+ Configuration = Pool().get('purchase.configuration')
+ configuration = Configuration(1)
+ if configuration.all_warehouse:
+ locations = Location.search([
+ ('type', '=', 'warehouse')
+ ])
+ location_ids = [l.storage_location.id for l in locations if l.storage_location]
+ elif self.purchase.warehouse:
+ location_ids = [self.purchase.warehouse.storage_location.id]
+ else:
+ return 0
+
+ product_ids = [self.product.id]
+ quantity = 0
+ with Transaction().set_context(context):
+ Product = Pool().get('product.product')
+ pbl = Product.products_by_location(
+ location_ids,
+ grouping=('product', ),
+ grouping_filter=(product_ids,))
+ for v in pbl.values():
+ quantity += v
+ quantity = quantity
+ return quantity
+
+
+class PurchaseAnalyticStart(ModelView):
+ 'Purchase Analytic Report Start'
+ __name__ = 'purchase_co.analytic.start'
+ company = fields.Many2One('company.company', 'Company', required=True)
+ start_date = fields.Date("Start Date", required=True)
+ end_date = fields.Date("End Date", required=True)
+
+ @staticmethod
+ def default_company():
+ return Transaction().context.get('company')
+
+ @staticmethod
+ def default_end_date():
+ Date = Pool().get('ir.date')
+ return Date.today()
+
+
+class PurchaseAnalytic(Wizard):
+ 'Purchase Analytic Report'
+ __name__ = 'purchase_co.analytic'
+ start = StateView('purchase_co.analytic.start',
+ 'purchase_co.purchase_analytic_start_view_form', [
+ Button('Cancel', 'end', 'tryton-cancel'),
+ Button('Print', 'print_', 'tryton-ok', default=True),
+ ])
+ print_ = StateReport('purchase_co.analytic.report')
+
+ def do_print_(self, action):
+ data = {
+ 'company': self.start.company.id,
+ 'start_date': self.start.start_date,
+ 'end_date': self.start.end_date,
+ }
+ return action, data
+
+ def transition_print_(self):
+ return 'end'
+
+
+class PurchaseAnalyticReport(Report):
+ __name__ = 'purchase_co.analytic.report'
+
+ @classmethod
+ def compute_amount_tax(cls, line):
+ Tax = Pool().get('account.tax')
+ tax_list = Tax.compute(Tax.browse(line['taxes']),
+ line['unit_price'] or Decimal('0.0'),
+ line['quantity'] or 0.0)
+ return sum([t['amount'] for t in tax_list], Decimal('0.0'))
+
+ @classmethod
+ def _get_rec(cls, line):
+ analytic_account = None
+ if line['analytic_accounts.']:
+ analytic_account = line['analytic_accounts.'][0]['account.']
+ inv_unit_price = Decimal(0)
+ if line['invoice_lines.']:
+ inv_unit_price = line['invoice_lines.'][0]['unit_price']
+
+ value = {
+ 'reference': line['purchase.']['reference'],
+ 'purchase_date': line['purchase.']['purchase_date'],
+ 'state': line['purchase.']['state'],
+ 'shipment_state': line['purchase.']['shipment_state'],
+ 'invoice_state': line['purchase.']['invoice_state'],
+ 'id_number': line['purchase.']['party.']['id_number'],
+ 'name': line['purchase.']['party.']['name'],
+ 'warehouse': line['purchase.']['warehouse.']['name'],
+ 'description': line['description'],
+ 'unit_name': line['unit.']['name'],
+ 'quantity': line['quantity'],
+ 'unit_price': line['unit_price'],
+ 'analytic_account': analytic_account['code'] + ' ' + analytic_account['name'] if analytic_account else '',
+ 'taxes': list(line['taxes']),
+ 'qty_received': sum([r['quantity'] for r in line['moves.']]),
+ 'amount': line['amount'],
+ 'inv_unit_price': inv_unit_price
+ }
+ tax_amount = cls.compute_amount_tax(value)
+ full_amount = value['amount'] + tax_amount
+ value.update({
+ 'tax_amount': tax_amount,
+ 'full_amount': full_amount,
+ })
+ return value
+
+ @classmethod
+ def get_context(cls, records, header, data):
+ report_context = super().get_context(records, header, data)
+ pool = Pool()
+ Company = pool.get('company.company')
+ PurchaseLine = pool.get('purchase.line')
+ fields_names = ['purchase.reference', 'purchase.purchase_date', 'purchase.party.id_number',
+ 'purchase.party.name', 'description', 'unit.name', 'quantity', 'unit_price',
+ 'purchase.state', 'purchase.shipment_state', 'purchase.invoice_state',
+ 'analytic_accounts.account.name', 'analytic_accounts.account.code', 'taxes',
+ 'invoice_lines.unit_price', 'moves.quantity', 'amount', 'purchase.warehouse.name'
+ ]
+
+ lines = PurchaseLine.search_read([
+ ('purchase.company', '=', data['company']),
+ ('purchase.purchase_date', '>=', data['start_date']),
+ ('purchase.purchase_date', '<=', data['end_date']),
+ ], fields_names=fields_names, order=[('purchase.purchase_date', 'ASC')])
+
+ records = []
+
+ records_append = records.append
+ get_rec = cls._get_rec
+ for line in lines:
+ records_append(get_rec(line))
+
+ report_context['records'] = records
+ report_context['company'] = Company(data['company'])
+ return report_context
+
+
+class PurchasesDetailedStart(ModelView):
+ 'Purchases Detailed Start'
+ __name__ = 'purchase.purchases_detailed.start'
+ company = fields.Many2One('company.company', 'Company', required=True)
+ start_date = fields.Date('Start Date', required=True)
+ end_date = fields.Date('End Date', required=True)
+ invoiced = fields.Boolean('Invoiced', help='Print purchase invoiced')
+ detailed = fields.Boolean('Detailed', help='Print report detailed')
+
+ @staticmethod
+ def default_company():
+ return Transaction().context.get('company')
+
+
+class PurchasesDetailed(Wizard):
+ 'Purchases Detailed'
+ __name__ = 'purchase.purchases_detailed'
+ start = StateView('purchase.purchases_detailed.start',
+ 'purchase_co.print_purchases_detailed_start_view_form', [
+ Button('Cancel', 'end', 'tryton-cancel'),
+ Button('Print', 'print_', 'tryton-ok', default=True),
+ ])
+ print_ = StateReport('purchase.purchases_detailed.report')
+
+ def do_print_(self, action):
+ data = {
+ 'company': self.start.company.id,
+ 'start_date': self.start.start_date,
+ 'end_date': self.start.end_date,
+ 'invoiced': self.start.invoiced,
+ 'detailed': self.start.detailed,
+ }
+ return action, data
+
+ def transition_print_(self):
+ return 'end'
+
+
+class PurchasesDetailedReport(Report):
+ __name__ = 'purchase.purchases_detailed.report'
+
+ @classmethod
+ def get_context(cls, records, header, data):
+ report_context = super().get_context(records, header, data)
+ pool = Pool()
+ Invoice = pool.get('account.invoice')
+
+ fields_names = [
+ 'number', 'description', 'party.name', 'untaxed_amount',
+ 'party.id_number', 'state', 'tax_amount', 'total_amount',
+ ]
+
+ lines = None
+
+ if data['invoiced']:
+ title = 'INFORME DE COMPRAS'
+
+ fields_names.append('invoice_date')
+ Invoiceline = pool.get('account.invoice.line')
+ dom_invoice = [
+ ('company', '=', data['company']),
+ ('invoice.invoice_date', '>=', data['start_date']),
+ ('invoice.invoice_date', '<=', data['end_date']),
+ ('invoice.type', '=', 'in'),
+ ('origin', 'ilike', 'purchase.line%'),
+ ]
+ fields_names_lines = ['id', 'invoice', 'origin.purchase.number']
+
+ fields = Invoiceline.fields_get(fields_names=['operation_center'])
+ if 'operation_center' in fields.keys():
+ fields_names_lines.append('operation_center.name')
+ lines = Invoiceline.search_read(dom_invoice, fields_names=fields_names_lines)
+
+ operation_centers = {}
+ invoices_ids = set()
+ invoices_ids_add = invoices_ids.add
+ oc_inv_update = operation_centers.update
+ for v in lines:
+ invoices_ids_add(v['invoice'])
+ operation_center = ''
+ if v['operation_center.']:
+ operation_center = v['operation_center.']['name']
+ purchase = v['origin.']['purchase.']['number']
+ oc_inv_update({v['invoice']: {'operation_center': operation_center, 'purchase': purchase}})
+
+ records = Invoice.search_read(
+ ['id', 'in', invoices_ids], fields_names=fields_names)
+
+ else:
+ fields_names.append('purchase_date')
+ Purchase = pool.get('purchase.purchase')
+ Purchaseline = pool.get('purchase.line')
+
+ title = 'INFORME DE ORDENES DE COMPRAS'
+
+ dom_lines = [
+ ('purchase.company', '=', data['company']),
+ ('purchase.purchase_date', '>=', data['start_date']),
+ ('purchase.purchase_date', '<=', data['end_date']),
+ ]
+ fields_names_lines = ['id', 'purchase']
+
+ fields = Purchaseline.fields_get(fields_names=['operation_center'])
+ if 'operation_center' in fields.keys():
+ fields_names_lines.append('operation_center.name')
+ lines = Purchaseline.search_read(dom_lines, fields_names=fields_names_lines)
+ operation_centers = {}
+ purchase_ids = set()
+ purchase_ids_add = purchase_ids.add
+ oc_purchase_update = operation_centers.update
+ for v in lines:
+ purchase_ids_add(v['id'])
+ operation_center = v.get('operation_center.', '')
+ if operation_center:
+ operation_center = operation_center['name']
+ oc_purchase_update({v['purchase']: operation_center})
+
+ dom_purchases = [
+ ('company', '=', data['company']),
+ ('purchase_date', '>=', data['start_date']),
+ ('purchase_date', '<=', data['end_date']),
+ ]
+ records = Purchase.search_read(dom_purchases, fields_names=fields_names,
+ order=[('party.name', 'ASC'), ('purchase_date', 'ASC')]
+ )
+
+ report_context['records'] = records
+ report_context['data'] = data
+ report_context['title'] = title
+ report_context['operation_centers'] = operation_centers
+ return report_context
diff --git a/purchase_detailed.fods b/purchases_detailed.fods
similarity index 100%
rename from purchase_detailed.fods
rename to purchases_detailed.fods
diff --git a/tryton.cfg b/tryton.cfg
index eb8452c..bdaa082 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -1,7 +1,8 @@
[tryton]
-version=6.0.1
+version=6.0.2
depends:
product
purchase
+ account_col
xml:
purchase.xml
diff --git a/view/print_purchase_detailed_start_form.xml b/view/print_purchase_detailed_start_form.xml
index ad57093..3068dbd 100644
--- a/view/print_purchase_detailed_start_form.xml
+++ b/view/print_purchase_detailed_start_form.xml
@@ -4,7 +4,7 @@ this repository contains the full copyright notices and license terms. -->