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.

211 lines
11 KiB

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