You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
11 KiB

  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Copyright (c) 2010 Camptocamp SA
  5. # All Rights Reserved
  6. #
  7. # Author : Vincent Renaville, ported by Joel Grand-Guillaume
  8. #
  9. # WARNING: This program as such is intended to be used by professional
  10. # programmers who take the whole responsability of assessing all potential
  11. # consequences resulting from its eventual inadequacies and bugs
  12. # End users who are looking for a ready-to-use solution with commercial
  13. # garantees and support are strongly adviced to contract a Free Software
  14. # Service Company
  15. #
  16. # This program is Free Software; you can redistribute it and/or
  17. # modify it under the terms of the GNU General Public License
  18. # as published by the Free Software Foundation; either version 2
  19. # of the License, or (at your option) any later version.
  20. #
  21. # This program is distributed in the hope that it will be useful,
  22. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. # GNU General Public License for more details.
  25. #
  26. # You should have received a copy of the GNU General Public License
  27. # along with this program; if not, write to the Free Software
  28. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  29. #
  30. ##############################################################################
  31. import time
  32. import string
  33. from osv import osv, fields
  34. import netsvc
  35. ############################################################################
  36. ## Add hours blocks on invoice
  37. ############################################################################
  38. class AccountHoursBlock(osv.osv):
  39. _name = "account.hours.block"
  40. def _get_last_action(self, cr, uid, ids, name, arg, context=None):
  41. """TODO"""
  42. context = context or {}
  43. res = {}
  44. for block in self.browse(cr, uid, ids):
  45. cr.execute("SELECT max(al.date) FROM account_analytic_line AS al"
  46. " WHERE al.invoice_id = %s", (block.invoice_id.id,))
  47. fetch_res = cr.fetchone()
  48. if fetch_res:
  49. date = fetch_res[0]
  50. else:
  51. date = False
  52. res[block.id] = date
  53. return res
  54. def _compute_hours(self, cr, uid, ids, fields, args, context=None):
  55. """Return a dict of [id][fields]"""
  56. context = context or {}
  57. if not isinstance(ids, list):
  58. ids=[ids]
  59. result = {}
  60. aal_obj = self.pool.get('account.analytic.line')
  61. for block in self.browse(cr,uid,ids):
  62. result[block.id] = {'amount_hours_block' : 0.0,
  63. 'amount_hours_block_done' : 0.0,
  64. 'amount_hours_block_delta' : 0.0}
  65. # Compute hours bought
  66. for line in block.invoice_id.invoice_line:
  67. hours_bought = 0.0
  68. if line.product_id:
  69. ## We will now calculate the product_quantity
  70. factor = line.uos_id.factor
  71. if factor == 0.0:
  72. factor = 1.0
  73. amount = line.quantity
  74. hours_bought += (amount / factor)
  75. result[block.id]['amount_hours_block'] += hours_bought
  76. # Compute hours spent
  77. hours_used = 0.0
  78. # Get ids of analytic line generated from timesheet associated to current block
  79. cr.execute("SELECT al.id "
  80. " FROM account_analytic_line AS al,account_analytic_journal AS aj"
  81. " WHERE aj.id = al.journal_id AND"
  82. " aj.type='general' AND"
  83. " al.invoice_id = %s", (block.invoice_id.id,))
  84. res2 = cr.fetchall()
  85. if res2:
  86. ids2 = [x[0] for x in res2]
  87. else:
  88. ids2 = []
  89. for line in aal_obj.browse(cr, uid, ids2, context):
  90. if line.product_uom_id:
  91. factor = line.product_uom_id.factor
  92. if factor == 0.0:
  93. factor = 1.0
  94. else:
  95. factor = 1.0
  96. factor_invoicing = 1.0
  97. if line.to_invoice and line.to_invoice.factor != 0.0:
  98. factor_invoicing = 1.0 - line.to_invoice.factor / 100
  99. hours_used += ((line.unit_amount / factor) * factor_invoicing)
  100. result[block.id]['amount_hours_block_done'] = hours_used
  101. return result
  102. def _compute_amount(self, cr, uid, ids, fields, args, context):
  103. result = {}
  104. aal_obj = self.pool.get('account.analytic.line')
  105. pricelist_obj = self.pool.get('product.pricelist')
  106. for block in self.browse(cr,uid,ids):
  107. result[block.id] = {'amount_hours_block' : 0.0,
  108. 'amount_hours_block_done' : 0.0,
  109. 'amount_hours_block_delta' : 0.0}
  110. # Compute amount bought
  111. for line in block.invoice_id.invoice_line:
  112. amount_bought = 0.0
  113. if line.product_id:
  114. ## We will now calculate the product_quantity
  115. factor = line.uos_id.factor
  116. if factor == 0.0:
  117. factor = 1.0
  118. amount = line.quantity * line.price_unit
  119. amount_bought += (amount / factor)
  120. result[block.id]['amount_hours_block'] += amount_bought
  121. # Compute total amount
  122. # Get ids of analytic line generated from timesheet associated to current block
  123. cr.execute("SELECT al.id FROM account_analytic_line AS al,"
  124. " account_analytic_journal AS aj"
  125. " WHERE aj.id = al.journal_id"
  126. " AND aj.type='general'"
  127. " AND al.invoice_id = %s", (block.invoice_id.id,))
  128. res2 = cr.fetchall()
  129. if res2:
  130. ids2 = [x[0] for x in res2]
  131. else:
  132. ids2 = []
  133. total_amount = 0.0
  134. for line in aal_obj.browse(cr, uid, ids2, context):
  135. factor_invoicing = 1.0
  136. if line.to_invoice and line.to_invoice.factor != 0.0:
  137. factor_invoicing = 1.0 - line.to_invoice.factor / 100
  138. ctx = {'uom': line.product_uom_id.id}
  139. amount = pricelist_obj.price_get(cr, uid,
  140. [line.account_id.pricelist_id.id],
  141. line.product_id.id,
  142. line.unit_amount or 1.0,
  143. line.account_id.partner_id.id or False,
  144. ctx)[line.account_id.pricelist_id.id]
  145. total_amount += amount * line.unit_amount * factor_invoicing
  146. result[block.id]['amount_hours_block_done'] += total_amount
  147. return result
  148. def _compute(self, cr, uid, ids, fields, args, context):
  149. result = {}
  150. block_per_types = {}
  151. for block in self.browse(cr, uid, ids, context=context):
  152. if not block.type in block_per_types.keys():
  153. block_per_types[block.type] = []
  154. block_per_types[block.type].append(block.id)
  155. for block_type in block_per_types:
  156. if block_type:
  157. func = getattr(self, "_compute_%s" % (block_type,))
  158. result.update(func(cr, uid, ids, fields, args, context))
  159. for block in result:
  160. result[block]['amount_hours_block_delta'] = \
  161. result[block]['amount_hours_block'] - result[block]['amount_hours_block_done']
  162. return result
  163. _columns = {
  164. 'amount_hours_block': fields.function(_compute, method=True, type='float', string='Quantity /Amount bought', store=True,
  165. multi='amount_hours_block_delta',
  166. help="Amount bought by the customer. This amount is expressed in the base UoM (factor=1.0)"),
  167. 'amount_hours_block_done': fields.function(_compute, method=True, type='float', string='Quantity / Amount used', store=True,
  168. multi='amount_hours_block_delta',
  169. help="Amount done by the staff. This amount is expressed in the base UoM (factor=1.0)"),
  170. 'amount_hours_block_delta': fields.function(_compute, method=True, type='float', string='Difference', store=True,
  171. multi='amount_hours_block_delta',
  172. help="Difference between bought and used. This amount is expressed in the base UoM (factor=1.0)"),
  173. 'last_action_date': fields.function(_get_last_action, method=True, type='date', string='Last action date',
  174. help="Date of the last analytic line linked to the invoice related to this block hours."),
  175. 'close_date': fields.date('Closed Date'),
  176. 'invoice_id': fields.many2one('account.invoice', 'Invoice', ondelete='cascade', required=True),
  177. 'type': fields.selection([('hours','Hours'), ('amount','Amount')], 'Type of Block',
  178. required=True, help="Choose if you want a time or amount base block."),
  179. # Invoices related infos
  180. 'date_invoice': fields.related('invoice_id', 'date_invoice', type="date", string="Invoice Date", store=True, readonly=True),
  181. 'user_id': fields.related('invoice_id', 'user_id', type="many2one", relation="res.users", string="Salesman", store=True, readonly=True),
  182. 'partner_id': fields.related('invoice_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True, readonly=True),
  183. 'name': fields.related('invoice_id', 'name', type="char",size=64, string="Description", store=True,readonly=True),
  184. 'number': fields.related('invoice_id', 'number', type="char",size=64, string="Number", store=True,readonly=True),
  185. 'journal_id': fields.related('invoice_id', 'journal_id', type="many2one", relation="account.journal", string="Journal", store=True,readonly=True),
  186. 'period_id': fields.related('invoice_id', 'period_id', type="many2one", relation="account.period", string="Period", store=True,readonly=True),
  187. 'company_id': fields.related('invoice_id', 'company_id', type="many2one", relation="res.company", string="Company", store=True,readonly=True),
  188. 'currency_id': fields.related('invoice_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", store=True,readonly=True),
  189. 'residual': fields.related('invoice_id', 'residual', type="float", string="Residual", store=True,readonly=True),
  190. 'amount_total': fields.related('invoice_id', 'amount_total', type="float", string="Total", store=True,readonly=True),
  191. 'state':fields.related('invoice_id','state',
  192. type='selection',
  193. selection=[
  194. ('draft','Draft'),
  195. ('proforma','Pro-forma'),
  196. ('proforma2','Pro-forma'),
  197. ('open','Open'),
  198. ('paid','Paid'),
  199. ('cancel','Cancelled')
  200. ],
  201. string='State', readonly=True, store=True),
  202. }
  203. AccountHoursBlock()
  204. class AccountInvoice(osv.osv):
  205. _inherit = 'account.invoice'
  206. _columns = {
  207. 'account_hours_block_ids': fields.one2many('account.hours.block', 'invoice_id', 'Hours Block')
  208. }
  209. AccountInvoice()