car_workshop.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Cybrosys Technologies Pvt. Ltd.
  5. # Copyright (C) 2008-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
  6. # Author: Nilmar Shereef(<http://www.cybrosys.com>)
  7. # you can modify it under the terms of the GNU LESSER
  8. # GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
  9. #
  10. # It is forbidden to publish, distribute, sublicense, or sell copies
  11. # of the Software or modified copies of the Software.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
  17. #
  18. # You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
  19. # GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
  20. # If not, see <http://www.gnu.org/licenses/>.
  21. #
  22. ##############################################################################
  23. from datetime import date
  24. from dateutil.relativedelta import relativedelta
  25. from openerp import models, api, fields, _
  26. from openerp.osv import osv
  27. class CarWorkshop(models.Model):
  28. _name = 'car.workshop'
  29. _inherit = ['mail.thread']
  30. name = fields.Char(string='Title', track_visibility='onchange', required=True)
  31. vehicle_id = fields.Many2one('car.car', string='Vehicle', track_visibility='onchange')
  32. user_id = fields.Many2one('res.users', string='Assigned to', select=True)
  33. active = fields.Boolean('Active')
  34. partner_id = fields.Many2one('res.partner', string='Customer')
  35. priority = fields.Selection([('0', 'Normal'), ('1', 'High')], string='Priority', select=True)
  36. description = fields.Html('Description')
  37. sequence = fields.Integer(string='Sequence', select=True, help="Gives the sequence order when displaying a list of tasks.")
  38. tag_ids = fields.Many2many('worksheet.tags', string='Tags')
  39. kanban_state = fields.Selection(
  40. [('normal', 'In Progress'), ('done', 'Ready for next stage'), ('blocked', 'Blocked')], string='Kanban State',
  41. help="A task's kanban state indicates special situations affecting it:\n"
  42. " * Normal is the default situation\n"
  43. " * Blocked indicates something is preventing the progress of this task\n"
  44. " * Ready for next stage indicates the task is ready to be pulled to the next stage",
  45. required=True, track_visibility='onchange', copy=False)
  46. create_date = fields.Datetime(string='Create Date', readonly=True, select=True)
  47. write_date = fields.Datetime(string='Last Modification Date', readonly=True, select=True)
  48. date_start = fields.Datetime(string='Starting Date', select=True, copy=False)
  49. date_end = fields.Datetime(string='Ending Date', select=True, copy=False)
  50. date_assign = fields.Datetime(string='Assigning Date', select=True, copy=False, readonly=True)
  51. date_deadline = fields.Datetime(string='Deadline', select=True, copy=False)
  52. progress = fields.Integer(string="Working Time Progress(%)", copy=False, readonly=True)
  53. date_last_stage_update = fields.Datetime(string='Last Stage Update', select=True, copy=False, readonly=True)
  54. id = fields.Integer(string='ID', readonly=True)
  55. color = fields.Integer(string='Color Index')
  56. company_id = fields.Many2one('res.company', string='Company',
  57. default=lambda self: self.env['res.company']._company_default_get('car.workshop'))
  58. stage_id = fields.Many2one('worksheet.stages', string='Stage', track_visibility='onchange', copy=False)
  59. state = fields.Selection([
  60. ('waiting', 'Ready'),
  61. ('workshop_create_invoices', 'Invoiced'),
  62. ('cancel', 'Invoice Canceled'),
  63. ], string='Status', readonly=True, default='waiting', track_visibility='onchange', select=True)
  64. planned_works = fields.One2many('planned.work', 'work_id', string='Planned/Ordered Works')
  65. works_done = fields.One2many('planned.work', 'work_id', string='Work Done', domain=[('completed', '=', True)])
  66. materials_used = fields.One2many('material.used', 'material_id', string='Materials Used')
  67. remaining_hour = fields.Float(string='Remaining Hour',readonly=True, compute="hours_left")
  68. effective_hour = fields.Float(string='Hours Spent', readonly=True, compute="hours_spent")
  69. amount_total = fields.Float(string='Total Amount', readonly=True, compute="amount_total1")
  70. include_materials = fields.Boolean('Incluir')
  71. _defaults = {
  72. 'stage_id': 1,
  73. 'vehicle_id': lambda self, cr, uid, ctx=None: ctx.get('default_vehicle_id') if ctx is not None else False,
  74. 'date_last_stage_update': fields.datetime.now(),
  75. 'kanban_state': 'normal',
  76. 'priority': '0',
  77. 'sequence': 10,
  78. 'active': True,
  79. 'user_id': lambda obj, cr, uid, ctx=None: uid,
  80. 'partner_id': lambda self, cr, uid, ctx=None: self._get_default_vehicle(cr, uid, context=ctx),
  81. 'date_start': fields.datetime.now(),
  82. }
  83. @api.depends('planned_works.work_cost', 'materials_used.price')
  84. def amount_total1(self):
  85. for records in self:
  86. for hour in records:
  87. amount_totall = 0.0
  88. for line in hour.planned_works:
  89. amount_totall += line.work_cost
  90. if self.include_materials==True:
  91. for line2 in hour.materials_used:
  92. amount_totall += line2.price
  93. records.amount_total = amount_totall
  94. @api.multi
  95. def cancel(self):
  96. self.state = 'cancel'
  97. @api.multi
  98. def workshop_create_invoices(self):
  99. self.state = 'workshop_create_invoices'
  100. inv_obj = self.env['account.invoice']
  101. inv_line_obj = self.env['account.invoice.line']
  102. customer = self.partner_id
  103. if not customer.name:
  104. raise osv.except_osv(_('UserError!'), _('Please select a Customer.'))
  105. company_id = self.env['res.users'].browse(1).company_id
  106. currency_value = company_id.currency_id.id
  107. self.ensure_one()
  108. ir_values = self.env['ir.values']
  109. journal_id = ir_values.get_default('workshop.config.setting', 'invoice_journal_type')
  110. if not journal_id:
  111. journal_id = 1
  112. inv_data = {
  113. 'name': customer.name,
  114. 'reference': customer.name,
  115. 'account_id': customer.property_account_receivable.id,
  116. 'partner_id': customer.id,
  117. 'currency_id': currency_value,
  118. 'journal_id': journal_id,
  119. 'origin': self.name,
  120. 'company_id': company_id.id,
  121. }
  122. inv_id = inv_obj.create(inv_data)
  123. for records in self.planned_works:
  124. if records.planned_work.id:
  125. income_account = records.planned_work.property_account_income.id
  126. if not income_account:
  127. raise osv.except_osv(_('UserError!'), _('There is no income account defined '
  128. 'for this product: "%s".') % (records.planned_work.name,))
  129. inv_line_data = {
  130. 'name': records.planned_work.name,
  131. 'account_id': income_account,
  132. 'price_unit': records.work_cost,
  133. 'quantity': 1,
  134. 'product_id': records.planned_work.id,
  135. 'invoice_id': inv_id.id,
  136. }
  137. inv_line_obj.create(inv_line_data)
  138. for records in self.materials_used:
  139. if records.material.id:
  140. income_account = records.material.property_account_income.id
  141. if not income_account:
  142. raise osv.except_osv(_('UserError!'), _('There is no income account defined '
  143. 'for this product: "%s".') % (records.material.name,))
  144. if self.include_materials==True:
  145. inv_line_data = {
  146. 'name': records.material.name,
  147. 'account_id': records.material.property_account_income.id,
  148. 'price_unit': records.price,
  149. 'quantity': records.amount,
  150. 'product_id': records.material.id,
  151. 'invoice_id': inv_id.id,
  152. }
  153. inv_line_obj.create(inv_line_data)
  154. imd = self.env['ir.model.data']
  155. action = imd.xmlid_to_object('account.action_invoice_tree1')
  156. list_view_id = imd.xmlid_to_res_id('account.invoice_tree')
  157. form_view_id = imd.xmlid_to_res_id('account.invoice_form')
  158. result = {
  159. 'name': action.name,
  160. 'help': action.help,
  161. 'type': 'ir.actions.act_window',
  162. 'views': [[list_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'kanban'],
  163. [False, 'calendar'], [False, 'pivot']],
  164. 'target': action.target,
  165. 'context': action.context,
  166. 'res_model': 'account.invoice',
  167. }
  168. if len(inv_id) > 1:
  169. result['domain'] = "[('id','in',%s)]" % inv_id.ids
  170. elif len(inv_id) == 1:
  171. result['views'] = [(form_view_id, 'form')]
  172. result['res_id'] = inv_id.ids[0]
  173. else:
  174. result = {'type': 'ir.actions.act_window_close'}
  175. invoiced_records = self.env['car.workshop']
  176. total = 0
  177. for rows in invoiced_records:
  178. invoiced_date = rows.date
  179. invoiced_date = invoiced_date[0:10]
  180. if invoiced_date == str(date.today()):
  181. total = total + rows.price_subtotal
  182. return result
  183. @api.depends('works_done.duration')
  184. def hours_spent(self):
  185. for hour in self:
  186. effective_hour = 0.0
  187. for line in hour.works_done:
  188. effective_hour += line.duration
  189. self.effective_hour = effective_hour
  190. @api.depends('planned_works.time_spent')
  191. def hours_left(self):
  192. for hour in self:
  193. remaining_hour = 0.0
  194. for line in hour.planned_works:
  195. remaining_hour += line.time_spent
  196. self.remaining_hour = remaining_hour-self.effective_hour
  197. def _track_subtype(self, cr, uid, ids, init_values, context=None):
  198. record = self.browse(cr, uid, ids[0], context=context)
  199. if 'kanban_state' in init_values and record.kanban_state == 'blocked':
  200. return 'fleet_car_workshop.mt_task_blocked'
  201. elif 'kanban_state' in init_values and record.kanban_state == 'done':
  202. return 'fleet_car_workshop.mt_task_ready'
  203. elif 'user_id' in init_values and record.user_id: # assigned -> new
  204. return 'fleet_car_workshop.mt_task_new'
  205. elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: # start stage -> new
  206. return 'fleet_car_workshop.mt_task_new'
  207. elif 'stage_id' in init_values:
  208. return 'fleet_car_workshop.mt_task_stage'
  209. return super(CarWorkshop, self)._track_subtype(cr, uid, ids, init_values, context=context)
  210. def create(self, cr, uid, vals, context=None):
  211. context = dict(context or {})
  212. if vals.get('vehicle_id') and not context.get('default_vehicle_id'):
  213. context['default_vehicle_id'] = vals.get('vehicle_id')
  214. if vals.get('user_id'):
  215. vals['date_assign'] = fields.datetime.now()
  216. create_context = dict(context, mail_create_nolog=True)
  217. work_id = super(CarWorkshop, self).create(cr, uid, vals, context=create_context)
  218. return work_id
  219. #
  220. def write(self, cr, uid, ids, vals, context=None):
  221. if isinstance(ids, (int, long)):
  222. ids = [ids]
  223. if 'stage_id' in vals:
  224. vals['date_last_stage_update'] = fields.datetime.now()
  225. vals['date_assign'] = fields.datetime.now()
  226. if vals and not'kanban_state' in vals and 'stage_id' in vals:
  227. new_stage = vals.get('stage_id')
  228. vals_reset_kstate = dict(vals, kanban_state='normal')
  229. for t in self.browse(cr, uid, ids, context=context):
  230. write_vals = vals_reset_kstate if t.stage_id.id != new_stage else vals
  231. super(CarWorkshop, self).write(cr, uid, [t.id], write_vals, context=context)
  232. result = True
  233. else:
  234. result = super(CarWorkshop, self).write(cr, uid, ids, vals, context=context)
  235. return result
  236. def _read_group_stages(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
  237. if context is None:
  238. context = {}
  239. stage_obj = self.pool.get('worksheet.stages')
  240. order = stage_obj._order
  241. access_rights_uid = access_rights_uid or uid
  242. if read_group_order == 'stage_id desc':
  243. order = '%s desc' % order
  244. if 'default_vehicle_id' in context:
  245. search_domain = ['|', ('vehicle_ids', '=', context['default_vehicle_id']), ('id', 'in', ids)]
  246. else:
  247. search_domain = [('id', 'in', ids)]
  248. stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
  249. result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
  250. # restore order of the search
  251. result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
  252. fold = {}
  253. for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
  254. fold[stage.id] = stage.fold or False
  255. return result, fold
  256. _group_by_full = {
  257. 'stage_id': _read_group_stages,
  258. }
  259. @api.cr_uid_ids_context
  260. def onchange_vehicle(self, cr, uid, id, vehicle_id, context=None):
  261. values = {}
  262. if vehicle_id:
  263. vehicle = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context)
  264. if vehicle.exists():
  265. values['partner_id'] = vehicle.partner_id.id
  266. else:
  267. values['stage_id'] = False
  268. return {'value': values}
  269. def _get_default_vehicle(self, cr, uid, context=None):
  270. if context is None:
  271. context = {}
  272. if 'default_vehicle_id' in context:
  273. vehicle = self.pool.get('car.car').browse(cr, uid, context['default_vehicle_id'], context=context)
  274. if vehicle and vehicle.partner_id:
  275. return vehicle.partner_id.id
  276. return False