car_workshop.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. attachment_ids = fields.One2many('ir.attachment', 'res_id', domain=lambda self: [('res_model', '=', self._name)],
  65. auto_join=True, string='Attachments')
  66. planned_works = fields.One2many('planned.work', 'work_id', string='Planned/Ordered Works')
  67. works_done = fields.One2many('planned.work', 'work_id', string='Work Done', domain=[('completed', '=', True)])
  68. materials_used = fields.One2many('material.used', 'material_id', string='Materials Used')
  69. remaining_hour = fields.Float(string='Remaining Hour',readonly=True, compute="hours_left")
  70. effective_hour = fields.Float(string='Hours Spent', readonly=True, compute="hours_spent")
  71. amount_total = fields.Float(string='Total Amount', readonly=True, compute="amount_total1")
  72. def _get_default_stages(self, cr, uid, context=None):
  73. """ Gives default stage_id """
  74. if context is None:
  75. context = {}
  76. default_vehicle_id = context.get('default_vehicle_id')
  77. if not default_vehicle_id:
  78. return False
  79. return self.find_stage(cr, uid, [], default_vehicle_id, [('fold', '=', False)], context=context)
  80. _defaults = {
  81. 'stage_id': _get_default_stages,
  82. 'vehicle_id': lambda self, cr, uid, ctx=None: ctx.get('default_vehicle_id') if ctx is not None else False,
  83. 'date_last_stage_update': fields.datetime.now(),
  84. 'kanban_state': 'normal',
  85. 'priority': '0',
  86. 'sequence': 10,
  87. 'active': True,
  88. 'user_id': lambda obj, cr, uid, ctx=None: uid,
  89. 'partner_id': lambda self, cr, uid, ctx=None: self._get_default_vehicle(cr, uid, context=ctx),
  90. 'date_start': fields.datetime.now(),
  91. }
  92. @api.depends('planned_works.work_cost', 'materials_used.price')
  93. def amount_total1(self):
  94. for records in self:
  95. for hour in records:
  96. amount_totall = 0.0
  97. for line in hour.planned_works:
  98. amount_totall += line.work_cost
  99. for line2 in hour.materials_used:
  100. amount_totall += line2.price
  101. records.amount_total = amount_totall
  102. @api.multi
  103. def cancel(self):
  104. self.state = 'cancel'
  105. @api.multi
  106. def workshop_create_invoices(self):
  107. self.state = 'workshop_create_invoices'
  108. inv_obj = self.env['account.invoice']
  109. inv_line_obj = self.env['account.invoice.line']
  110. customer = self.partner_id
  111. if not customer.name:
  112. raise osv.except_osv(_('UserError!'), _('Please select a Customer.'))
  113. company_id = self.env['res.users'].browse(1).company_id
  114. currency_value = company_id.currency_id.id
  115. self.ensure_one()
  116. ir_values = self.env['ir.values']
  117. journal_id = ir_values.get_default('workshop.config.setting', 'invoice_journal_type')
  118. if not journal_id:
  119. journal_id = 1
  120. inv_data = {
  121. 'name': customer.name,
  122. 'reference': customer.name,
  123. 'account_id': customer.property_account_receivable.id,
  124. 'partner_id': customer.id,
  125. 'currency_id': currency_value,
  126. 'journal_id': journal_id,
  127. 'origin': self.name,
  128. 'company_id': company_id.id,
  129. }
  130. inv_id = inv_obj.create(inv_data)
  131. for records in self.planned_works:
  132. if records.planned_work.id:
  133. income_account = records.planned_work.property_account_income.id
  134. if not income_account:
  135. raise osv.except_osv(_('UserError!'), _('There is no income account defined '
  136. 'for this product: "%s".') % (records.planned_work.name,))
  137. inv_line_data = {
  138. 'name': records.planned_work.name,
  139. 'account_id': income_account,
  140. 'price_unit': records.work_cost,
  141. 'quantity': 1,
  142. 'product_id': records.planned_work.id,
  143. 'invoice_id': inv_id.id,
  144. }
  145. inv_line_obj.create(inv_line_data)
  146. for records in self.materials_used:
  147. if records.material.id:
  148. income_account = records.material.property_account_income.id
  149. if not income_account:
  150. raise osv.except_osv(_('UserError!'), _('There is no income account defined '
  151. 'for this product: "%s".') % (records.material.name,))
  152. inv_line_data = {
  153. 'name': records.material.name,
  154. 'account_id': records.material.property_account_income.id,
  155. 'price_unit': records.price,
  156. 'quantity': records.amount,
  157. 'product_id': records.material.id,
  158. 'invoice_id': inv_id.id,
  159. }
  160. inv_line_obj.create(inv_line_data)
  161. imd = self.env['ir.model.data']
  162. action = imd.xmlid_to_object('account.action_invoice_tree1')
  163. list_view_id = imd.xmlid_to_res_id('account.invoice_tree')
  164. form_view_id = imd.xmlid_to_res_id('account.invoice_form')
  165. result = {
  166. 'name': action.name,
  167. 'help': action.help,
  168. 'type': 'ir.actions.act_window',
  169. 'views': [[list_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'kanban'],
  170. [False, 'calendar'], [False, 'pivot']],
  171. 'target': action.target,
  172. 'context': action.context,
  173. 'res_model': 'account.invoice',
  174. }
  175. if len(inv_id) > 1:
  176. result['domain'] = "[('id','in',%s)]" % inv_id.ids
  177. elif len(inv_id) == 1:
  178. result['views'] = [(form_view_id, 'form')]
  179. result['res_id'] = inv_id.ids[0]
  180. else:
  181. result = {'type': 'ir.actions.act_window_close'}
  182. invoiced_records = self.env['car.workshop']
  183. total = 0
  184. for rows in invoiced_records:
  185. invoiced_date = rows.date
  186. invoiced_date = invoiced_date[0:10]
  187. if invoiced_date == str(date.today()):
  188. total = total + rows.price_subtotal
  189. print result
  190. return result
  191. @api.depends('works_done.duration')
  192. def hours_spent(self):
  193. for hour in self:
  194. effective_hour = 0.0
  195. for line in hour.works_done:
  196. effective_hour += line.duration
  197. self.effective_hour = effective_hour
  198. @api.depends('planned_works.time_spent')
  199. def hours_left(self):
  200. for hour in self:
  201. remaining_hour = 0.0
  202. for line in hour.planned_works:
  203. remaining_hour += line.time_spent
  204. self.remaining_hour = remaining_hour-self.effective_hour
  205. def process_demo_scheduler_queue(self, cr, uid, context=None):
  206. obj = self.pool.get('car.workshop')
  207. obj1 = obj.search(cr, uid, [])
  208. now = fields.Datetime.from_string(fields.Datetime.now())
  209. for obj2 in obj1:
  210. obj3 = obj.browse(cr, uid, obj2, context=context)
  211. if obj3.stage_id.name != 'Done' and obj3.stage_id.name != 'Cancelled' and obj3.stage_id.name != 'Verified':
  212. start_date = fields.Datetime.from_string(obj3.date_start)
  213. end_date = fields.Datetime.from_string(obj3.date_deadline)
  214. if obj3.date_deadline and end_date > start_date:
  215. if now < end_date:
  216. diff1 = relativedelta(end_date, start_date)
  217. if diff1.days == 0:
  218. total_hr = int(diff1.minutes)
  219. else:
  220. total_hr = int(diff1.days) * 24 * 60 + int(diff1.minutes)
  221. diff2 = relativedelta(now, start_date)
  222. if diff2.days == 0:
  223. current_hr = int(diff2.minutes)
  224. else:
  225. current_hr = int(diff2.days) * 24 * 60 + int(diff2.minutes)
  226. if total_hr != 0:
  227. obj3.progress = ((current_hr * 100) / total_hr)
  228. else:
  229. obj3.progress = 100
  230. else:
  231. obj3.progress = 100
  232. else:
  233. obj3.progress = 0
  234. def _track_subtype(self, cr, uid, ids, init_values, context=None):
  235. record = self.browse(cr, uid, ids[0], context=context)
  236. if 'kanban_state' in init_values and record.kanban_state == 'blocked':
  237. return 'fleet_car_workshop.mt_task_blocked'
  238. elif 'kanban_state' in init_values and record.kanban_state == 'done':
  239. return 'fleet_car_workshop.mt_task_ready'
  240. elif 'user_id' in init_values and record.user_id: # assigned -> new
  241. return 'fleet_car_workshop.mt_task_new'
  242. elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: # start stage -> new
  243. return 'fleet_car_workshop.mt_task_new'
  244. elif 'stage_id' in init_values:
  245. return 'fleet_car_workshop.mt_task_stage'
  246. return super(CarWorkshop, self)._track_subtype(cr, uid, ids, init_values, context=context)
  247. def create(self, cr, uid, vals, context=None):
  248. context = dict(context or {})
  249. if vals.get('vehicle_id') and not context.get('default_vehicle_id'):
  250. context['default_vehicle_id'] = vals.get('vehicle_id')
  251. if vals.get('user_id'):
  252. vals['date_assign'] = fields.datetime.now()
  253. create_context = dict(context, mail_create_nolog=True)
  254. work_id = super(CarWorkshop, self).create(cr, uid, vals, context=create_context)
  255. return work_id
  256. #
  257. def write(self, cr, uid, ids, vals, context=None):
  258. if isinstance(ids, (int, long)):
  259. ids = [ids]
  260. if 'stage_id' in vals:
  261. vals['date_last_stage_update'] = fields.datetime.now()
  262. vals['date_assign'] = fields.datetime.now()
  263. if vals and not'kanban_state' in vals and 'stage_id' in vals:
  264. new_stage = vals.get('stage_id')
  265. vals_reset_kstate = dict(vals, kanban_state='normal')
  266. for t in self.browse(cr, uid, ids, context=context):
  267. write_vals = vals_reset_kstate if t.stage_id.id != new_stage else vals
  268. super(CarWorkshop, self).write(cr, uid, [t.id], write_vals, context=context)
  269. result = True
  270. else:
  271. result = super(CarWorkshop, self).write(cr, uid, ids, vals, context=context)
  272. return result
  273. def _read_group_stages(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
  274. if context is None:
  275. context = {}
  276. stage_obj = self.pool.get('worksheet.stages')
  277. order = stage_obj._order
  278. access_rights_uid = access_rights_uid or uid
  279. if read_group_order == 'stage_id desc':
  280. order = '%s desc' % order
  281. if 'default_vehicle_id' in context:
  282. search_domain = ['|', ('vehicle_ids', '=', context['default_vehicle_id']), ('id', 'in', ids)]
  283. else:
  284. search_domain = [('id', 'in', ids)]
  285. stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
  286. result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
  287. # restore order of the search
  288. result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
  289. fold = {}
  290. for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
  291. fold[stage.id] = stage.fold or False
  292. return result, fold
  293. _group_by_full = {
  294. 'stage_id': _read_group_stages,
  295. }
  296. @api.cr_uid_ids_context
  297. def onchange_vehicle(self, cr, uid, id, vehicle_id, context=None):
  298. values = {}
  299. if vehicle_id:
  300. vehicle = self.pool.get('fleet.vehicle').browse(cr, uid, vehicle_id, context=context)
  301. if vehicle.exists():
  302. values['partner_id'] = vehicle.partner_id.id
  303. values['stage_id'] = self.find_stage(cr, uid, [], vehicle_id, [('fold', '=', False)], context=context)
  304. else:
  305. values['stage_id'] = False
  306. return {'value': values}
  307. def _get_default_vehicle(self, cr, uid, context=None):
  308. if context is None:
  309. context = {}
  310. if 'default_vehicle_id' in context:
  311. vehicle = self.pool.get('car.car').browse(cr, uid, context['default_vehicle_id'], context=context)
  312. if vehicle and vehicle.partner_id:
  313. return vehicle.partner_id.id
  314. return False
  315. def find_stage(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
  316. if isinstance(cases, (int, long)):
  317. cases = self.browse(cr, uid, cases, context=context)
  318. section_ids = []
  319. if section_id:
  320. section_ids.append(section_id)
  321. for task in cases:
  322. if task.vehicle_id:
  323. section_ids.append(task.vehicle_id.id)
  324. search_domain = []
  325. if section_ids:
  326. search_domain = [('|')] * (len(section_ids) - 1)
  327. for section_id in section_ids:
  328. search_domain.append(('vehicle_ids', '=', section_id))
  329. search_domain += list(domain)
  330. # perform search, return the first found
  331. stage_ids = self.pool.get('worksheet.stages').search(cr, uid, search_domain, order=order, context=context)
  332. if stage_ids:
  333. return stage_ids[0]
  334. return False