예제 #1
0
파일: resource.py 프로젝트: ecoreos/hz
class resource_calendar_leaves(osv.osv):
    _name = "resource.calendar.leaves"
    _description = "Leave Detail"
    _columns = {
        'name':
        fields.char("Name"),
        'company_id':
        fields.related('calendar_id',
                       'company_id',
                       type='many2one',
                       relation='res.company',
                       string="Company",
                       store=True,
                       readonly=True),
        'calendar_id':
        fields.many2one("resource.calendar", "Working Time"),
        'date_from':
        fields.datetime('Start Date', required=True),
        'date_to':
        fields.datetime('End Date', required=True),
        'resource_id':
        fields.many2one(
            "resource.resource",
            "Resource",
            help=
            "If empty, this is a generic holiday for the company. If a resource is set, the holiday/leave is only for this resource"
        ),
    }

    def check_dates(self, cr, uid, ids, context=None):
        for leave in self.browse(cr, uid, ids, context=context):
            if leave.date_from and leave.date_to and leave.date_from > leave.date_to:
                return False
        return True

    _constraints = [
        (check_dates,
         'Error! leave start-date must be lower then leave end-date.',
         ['date_from', 'date_to'])
    ]

    def onchange_resource(self, cr, uid, ids, resource, context=None):
        result = {}
        if resource:
            resource_pool = self.pool.get('resource.resource')
            result['calendar_id'] = resource_pool.browse(
                cr, uid, resource, context=context).calendar_id.id
            return {'value': result}
        return {'value': {'calendar_id': []}}
예제 #2
0
class res_currency_rate(osv.osv):
    _name = "res.currency.rate"
    _description = "Currency Rate"

    _columns = {
        'name': fields.datetime('Date', required=True, select=True),
        'rate': fields.float('Rate', digits=(12, 6), help='The rate of the currency to the currency of rate 1'),
        'currency_id': fields.many2one('res.currency', 'Currency', readonly=True),
        'company_id': fields.many2one('res.company', 'Company')
    }
    _defaults = {
        'name': lambda *a: time.strftime('%Y-%m-%d 00:00:00'),
    }
    _order = "name desc"

    def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
        if operator in ['=', '!=']:
            try:
                date_format = '%Y-%m-%d'
                if context.get('lang'):
                    lang_obj = self.pool['res.lang']
                    lang_ids = lang_obj.search(cr, user, [('code', '=', context['lang'])], context=context)
                    if lang_ids:
                        date_format = lang_obj.browse(cr, user, lang_ids[0], context=context).date_format
                name = time.strftime('%Y-%m-%d', time.strptime(name, date_format))
            except ValueError:
                try:
                    args.append(('rate', operator, float(name)))
                except ValueError:
                    return []
                name = ''
                operator = 'ilike'
        return super(res_currency_rate, self).name_search(cr, user, name, args=args, operator=operator, context=context, limit=limit)
예제 #3
0
파일: ir_logging.py 프로젝트: LiberTang0/5
class ir_logging(osv.Model):
    _name = 'ir.logging'
    _order = 'id DESC'

    EXCEPTIONS_TYPE = [('client', 'Client'), ('server', 'Server')]

    _columns = {
        'create_date':
        fields.datetime('Create Date', readonly=True),
        'create_uid':
        fields.integer('Uid',
                       readonly=True),  # Integer not m2o is intentionnal
        'name':
        fields.char('Name', required=True),
        'type':
        fields.selection(EXCEPTIONS_TYPE,
                         string='Type',
                         required=True,
                         select=True),
        'dbname':
        fields.char('Database Name', select=True),
        'level':
        fields.char('Level', select=True),
        'message':
        fields.text('Message', required=True),
        'path':
        fields.char('Path', required=True),
        'func':
        fields.char('Function', required=True),
        'line':
        fields.char('Line', required=True),
    }
예제 #4
0
class ir_model_fields_anonymization_history(osv.osv):
    _name = 'ir.model.fields.anonymization.history'
    _order = "date desc"

    _columns = {
        'date':
        fields.datetime('Date', required=True, readonly=True),
        'field_ids':
        fields.many2many('ir.model.fields.anonymization',
                         'anonymized_field_to_history_rel',
                         'field_id',
                         'history_id',
                         'Fields',
                         readonly=True),
        'state':
        fields.selection(selection=ANONYMIZATION_HISTORY_STATE,
                         string='Status',
                         required=True,
                         readonly=True),
        'direction':
        fields.selection(selection=ANONYMIZATION_DIRECTION,
                         string='Direction',
                         size=20,
                         required=True,
                         readonly=True),
        'msg':
        fields.text('Message', readonly=True),
        'filepath':
        fields.char(string='File path', readonly=True),
    }
예제 #5
0
class wizard_valuation_history(osv.osv_memory):

    _name = 'wizard.valuation.history'
    _description = 'Wizard that opens the stock valuation history table'
    _columns = {
        'choose_date': fields.boolean('Inventory at Date'),
        'date': fields.datetime('Date', required=True),
    }

    _defaults = {
        'choose_date': False,
        'date': fields.datetime.now,
    }

    def open_table(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        data = self.read(cr, uid, ids, context=context)[0]
        ctx = context.copy()
        ctx['history_date'] = data['date']
        ctx['search_default_group_by_product'] = True
        ctx['search_default_group_by_location'] = True
        return {
            'domain': "[('date', '<=', '" + data['date'] + "')]",
            'name': _('Stock Value At Date'),
            'view_type': 'form',
            'view_mode': 'tree',
            'res_model': 'stock.history',
            'type': 'ir.actions.act_window',
            'context': ctx,
        }
예제 #6
0
class ResPartner(osv.Model):
    _inherit = 'res.partner'

    _columns = {
        'id': fields.integer('Id', readonly=True),
        'create_date': fields.datetime('Create Date', readonly=True),
    }
예제 #7
0
class MassMailingReport(osv.Model):
    _name = 'mail.statistics.report'
    _auto = False
    _description = 'Mass Mailing Statistics'

    _columns = {
        'scheduled_date':
        fields.datetime('Scheduled Date', readonly=True),
        'name':
        fields.char('Mass Mail', readonly=True),
        'campaign':
        fields.char('Mass Mail Campaign', readonly=True),
        'sent':
        fields.integer('Sent', readonly=True),
        'delivered':
        fields.integer('Delivered', readonly=True),
        'opened':
        fields.integer('Opened', readonly=True),
        'bounced':
        fields.integer('Bounced', readonly=True),
        'replied':
        fields.integer('Replied', readonly=True),
        'state':
        fields.selection(
            [('draft', 'Draft'), ('test', 'Tested'), ('done', 'Sent')],
            string='Status',
            readonly=True,
        ),
        'email_from':
        fields.char('From', readonly=True),
    }

    def init(self, cr):
        """Mass Mail Statistical Report: based on mail.mail.statistics that models the various
        statistics collected for each mailing, and mail.mass_mailing model that models the
        various mailing performed. """
        tools.drop_view_if_exists(cr, 'mail_statistics_report')
        cr.execute("""
            CREATE OR REPLACE VIEW mail_statistics_report AS (
                SELECT
                    min(ms.id) as id,
                    ms.scheduled as scheduled_date,
                    mm.name as name,
                    mc.name as campaign,
                    count(ms.bounced) as bounced,
                    count(ms.sent) as sent,
                    (count(ms.sent) - count(ms.bounced)) as delivered,
                    count(ms.opened) as opened,
                    count(ms.replied) as replied,
                    mm.state,
                    mm.email_from
                FROM
                    mail_mail_statistics as ms
                    left join mail_mass_mailing as mm ON (ms.mass_mailing_id=mm.id)
                    left join mail_mass_mailing_campaign as mc ON (ms.mass_mailing_campaign_id=mc.id)
                GROUP BY ms.scheduled, mm.name, mc.name, mm.state, mm.email_from
            )""")
예제 #8
0
class lead_test(osv.Model):
    _name = "base.action.rule.lead.test"
    _description = "Action Rule Test"

    _columns = {
        'name':
        fields.char('Subject', required=True, select=1),
        'user_id':
        fields.many2one('res.users', 'Responsible'),
        'state':
        fields.selection(AVAILABLE_STATES, string="Status", readonly=True),
        'active':
        fields.boolean('Active', required=False),
        'partner_id':
        fields.many2one('res.partner', 'Partner', ondelete='set null'),
        'date_action_last':
        fields.datetime('Last Action', readonly=1),
        'line_ids':
        fields.one2many('base.action.rule.line.test', 'lead_id'),
    }

    _defaults = {
        'state': 'draft',
        'active': True,
    }

    customer = ecore.fields.Boolean(related='partner_id.customer',
                                    readonly=True,
                                    store=True)

    @api.cr_uid_ids_context
    def message_post(self,
                     cr,
                     uid,
                     thread_id,
                     body='',
                     subject=None,
                     message_type='notification',
                     subtype=None,
                     parent_id=False,
                     attachments=None,
                     context=None,
                     **kwargs):
        pass

    def message_subscribe(self,
                          cr,
                          uid,
                          ids,
                          partner_ids=None,
                          channel_ids=None,
                          subtype_ids=None,
                          force=True,
                          context=None):
        pass
예제 #9
0
class MassMailingList(osv.Model):
    """Model of a contact list. """
    _name = 'mail.mass_mailing.list'
    _order = 'name'
    _description = 'Mailing List'

    def _get_contact_nbr(self, cr, uid, ids, name, arg, context=None):
        result = dict.fromkeys(ids, 0)
        Contacts = self.pool.get('mail.mass_mailing.contact')
        for group in Contacts.read_group(cr,
                                         uid, [('list_id', 'in', ids),
                                               ('opt_out', '!=', True)],
                                         ['list_id'], ['list_id'],
                                         context=context):
            result[group['list_id'][0]] = group['list_id_count']
        return result

    _columns = {
        'name':
        fields.char('Mailing List', required=True),
        'active':
        fields.boolean('Active'),
        'create_date':
        fields.datetime('Creation Date'),
        'contact_nbr':
        fields.function(
            _get_contact_nbr,
            type='integer',
            string='Number of Contacts',
        ),
        'popup_content':
        fields.html("Website Popup Content",
                    translate=True,
                    required=True,
                    sanitize=False),
        'popup_redirect_url':
        fields.char("Website Popup Redirect URL"),
    }

    def _get_default_popup_content(self, cr, uid, context=None):
        return """<div class="modal-header text-center">
    <h3 class="modal-title mt8">eCore Presents</h3>
</div>
<div class="o_popup_message">
    <font>7</font>
    <strong>Business Hacks</strong>
    <span> to<br/>boost your marketing</span>
</div>
<p class="o_message_paragraph">Join our Marketing newsletter and get <strong>this white paper instantly</strong></p>"""

    _defaults = {
        'active': True,
        'popup_content': _get_default_popup_content,
        'popup_redirect_url': '/',
    }
예제 #10
0
파일: report_stock.py 프로젝트: ecoreos/hz
class report_stock_lines_date(osv.osv):
    _name = "report.stock.lines.date"
    _description = "Dates of Inventories and latest Moves"
    _auto = False
    _order = "date"
    _columns = {
        'id':
        fields.integer('Product Id', readonly=True),
        'product_id':
        fields.many2one('product.product',
                        'Product',
                        readonly=True,
                        select=True),
        'date':
        fields.datetime('Date of latest Inventory', readonly=True),
        'move_date':
        fields.datetime('Date of latest Stock Move', readonly=True),
        "active":
        fields.boolean("Active", readonly=True),
    }

    def init(self, cr):
        drop_view_if_exists(cr, 'report_stock_lines_date')
        cr.execute("""
            create or replace view report_stock_lines_date as (
                select
                p.id as id,
                p.id as product_id,
                max(s.date) as date,
                max(m.date) as move_date,
                p.active as active
            from
                product_product p
                    left join (
                        stock_inventory_line l
                        inner join stock_inventory s on (l.inventory_id=s.id and s.state = 'done')
                    ) on (p.id=l.product_id)
                    left join stock_move m on (m.product_id=p.id and m.state = 'done')
                group by p.id
            )""")
예제 #11
0
class TestFunctionCounter(osv.Model):
    _name = 'test_old_api.function_counter'

    def _compute_cnt(self, cr, uid, ids, fname, arg, context=None):
        res = {}
        for cnt in self.browse(cr, uid, ids, context=context):
            res[cnt.id] = cnt.access and cnt.cnt + 1 or 0
        return res

    _columns = {
        'access': fields.datetime('Datetime Field'),
        'cnt': fields.function(
            _compute_cnt, type='integer', string='Function Field', store=True),
    }
예제 #12
0
파일: stock.py 프로젝트: ecoreos/hz
class stock_warehouse_orderpoint(osv.osv):
    _inherit = "stock.warehouse.orderpoint"

    _columns = {
        'calendar_id':
        fields.many2one(
            'resource.calendar',
            'Calendar',
            help=
            "In the calendar you can define the days that the goods will be delivered.  That way the scheduler will only take into account the goods needed until the second delivery and put the procurement date as the first delivery.  "
        ),
        'purchase_calendar_id':
        fields.many2one('resource.calendar', 'Purchase Calendar'),
        'last_execution_date':
        fields.datetime('Last Execution Date', readonly=True),
    }
예제 #13
0
파일: badge.py 프로젝트: LiberTang0/5
class gamification_badge_user(osv.Model):
    """User having received a badge"""

    _name = 'gamification.badge.user'
    _description = 'Gamification user badge'
    _order = "create_date desc"
    _rec_name = "badge_name"

    _columns = {
        'user_id': fields.many2one('res.users', string="User", required=True, ondelete="cascade"),
        'sender_id': fields.many2one('res.users', string="Sender", help="The user who has send the badge"),
        'badge_id': fields.many2one('gamification.badge', string='Badge', required=True, ondelete="cascade"),
        'challenge_id': fields.many2one('gamification.challenge', string='Challenge originating', help="If this badge was rewarded through a challenge"),
        'comment': fields.text('Comment'),
        'badge_name': fields.related('badge_id', 'name', type="char", string="Badge Name"),
        'create_date': fields.datetime('Created', readonly=True),
        'create_uid': fields.many2one('res.users', string='Creator', readonly=True),
    }


    def _send_badge(self, cr, uid, ids, context=None):
        """Send a notification to a user for receiving a badge

        Does not verify constrains on badge granting.
        The users are added to the owner_ids (create badge_user if needed)
        The stats counters are incremented
        :param ids: list(int) of badge users that will receive the badge
        """
        res = True
        temp_obj = self.pool.get('mail.template')
        user_obj = self.pool.get('res.users')
        template_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'gamification', 'email_template_badge_received')[1]
        for badge_user in self.browse(cr, uid, ids, context=context):
            template = temp_obj.get_email_template(cr, uid, template_id, badge_user.id, context=context)
            body_html = temp_obj.render_template(cr, uid, template.body_html, 'gamification.badge.user', badge_user.id, context=template._context)
            res = user_obj.message_post(
                cr, uid, badge_user.user_id.id,
                body=body_html,
                subtype='gamification.mt_badge_granted',
                partner_ids=[badge_user.user_id.partner_id.id],
                context=context)
        return res

    def create(self, cr, uid, vals, context=None):
        self.pool.get('gamification.badge').check_granting(cr, uid, badge_id=vals.get('badge_id'), context=context)
        return super(gamification_badge_user, self).create(cr, uid, vals, context=context)
예제 #14
0
파일: subscription.py 프로젝트: ecoreos/hz
class subscription_subscription_history(osv.osv):
    _name = "subscription.subscription.history"
    _description = "Subscription history"
    _rec_name = 'date'
    _columns = {
        'date':
        fields.datetime('Date'),
        'subscription_id':
        fields.many2one('subscription.subscription',
                        'Subscription',
                        ondelete='cascade'),
        'document_id':
        fields.reference('Source Document',
                         required=True,
                         selection=_get_document_types,
                         size=128),
    }
예제 #15
0
파일: test_models.py 프로젝트: ecoreos/hz
class test_converter(orm.Model):
    _name = 'web_editor.converter.test'

    # disable translation export for those brilliant field labels and values
    _translate = False

    _columns = {
        'char':
        fields.char(),
        'integer':
        fields.integer(),
        'float':
        fields.float(),
        'numeric':
        fields.float(digits=(16, 2)),
        'many2one':
        fields.many2one('web_editor.converter.test.sub'),
        'binary':
        fields.binary(),
        'date':
        fields.date(),
        'datetime':
        fields.datetime(),
        'selection':
        fields.selection([
            (1, "réponse A"),
            (2, "réponse B"),
            (3, "réponse C"),
            (4, "réponse D"),
        ]),
        'selection_str':
        fields.selection(
            [
                ('A', "Qu'il n'est pas arrivé à Toronto"),
                ('B', "Qu'il était supposé arriver à Toronto"),
                ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
                ('D', "La réponse D"),
            ],
            string=
            u"Lorsqu'un pancake prend l'avion à destination de Toronto et "
            u"qu'il fait une escale technique à St Claude, on dit:"),
        'html':
        fields.html(),
        'text':
        fields.text(),
    }
예제 #16
0
class stock_history(osv.osv):
    _name = 'stock.history'
    _auto = False
    _order = 'date asc'

    def read_group(self,
                   cr,
                   uid,
                   domain,
                   fields,
                   groupby,
                   offset=0,
                   limit=None,
                   context=None,
                   orderby=False,
                   lazy=True):
        res = super(stock_history, self).read_group(cr,
                                                    uid,
                                                    domain,
                                                    fields,
                                                    groupby,
                                                    offset=offset,
                                                    limit=limit,
                                                    context=context,
                                                    orderby=orderby,
                                                    lazy=lazy)
        if context is None:
            context = {}
        date = context.get('history_date', datetime.now())
        if 'inventory_value' in fields:
            group_lines = {}
            for line in res:
                domain = line.get('__domain', [])
                group_lines.setdefault(
                    str(domain), self.search(cr, uid, domain, context=context))
            line_ids = set()
            for ids in group_lines.values():
                for product_id in ids:
                    line_ids.add(product_id)
            line_ids = list(line_ids)
            lines_rec = {}
            if line_ids:
                cr.execute(
                    'SELECT id, product_id, price_unit_on_quant, company_id, quantity FROM stock_history WHERE id in %s',
                    (tuple(line_ids), ))
                lines_rec = cr.dictfetchall()
            lines_dict = dict((line['id'], line) for line in lines_rec)
            product_ids = list(
                set(line_rec['product_id'] for line_rec in lines_rec))
            products_rec = self.pool['product.product'].read(
                cr, uid, product_ids, ['cost_method', 'id'], context=context)
            products_dict = dict(
                (product['id'], product) for product in products_rec)
            cost_method_product_ids = list(
                set(product['id'] for product in products_rec
                    if product['cost_method'] != 'real'))
            histories = []
            if cost_method_product_ids:
                cr.execute(
                    'SELECT DISTINCT ON (product_id, company_id) product_id, company_id, cost FROM product_price_history WHERE product_id in %s AND datetime <= %s ORDER BY product_id, company_id, datetime DESC',
                    (tuple(cost_method_product_ids), date))
                histories = cr.dictfetchall()
            histories_dict = {}
            for history in histories:
                histories_dict[(history['product_id'],
                                history['company_id'])] = history['cost']
            for line in res:
                inv_value = 0.0
                lines = group_lines.get(str(line.get('__domain', [])))
                for line_id in lines:
                    line_rec = lines_dict[line_id]
                    product = products_dict[line_rec['product_id']]
                    if product['cost_method'] == 'real':
                        price = line_rec['price_unit_on_quant']
                    else:
                        price = histories_dict.get(
                            (product['id'], line_rec['company_id']), 0.0)
                    inv_value += price * line_rec['quantity']
                line['inventory_value'] = inv_value
        return res

    def _get_inventory_value(self, cr, uid, ids, name, attr, context=None):
        if context is None:
            context = {}
        date = context.get('history_date')
        product_obj = self.pool.get("product.product")
        res = {}
        for line in self.browse(cr, uid, ids, context=context):
            if line.product_id.cost_method == 'real':
                res[line.id] = line.quantity * line.price_unit_on_quant
            else:
                res[line.id] = line.quantity * product_obj.get_history_price(
                    cr,
                    uid,
                    line.product_id.id,
                    line.company_id.id,
                    date=date,
                    context=context)
        return res

    _columns = {
        'move_id':
        fields.many2one('stock.move', 'Stock Move', required=True),
        'location_id':
        fields.many2one('stock.location', 'Location', required=True),
        'company_id':
        fields.many2one('res.company', 'Company'),
        'product_id':
        fields.many2one('product.product', 'Product', required=True),
        'product_categ_id':
        fields.many2one('product.category', 'Product Category', required=True),
        'quantity':
        fields.float('Product Quantity'),
        'date':
        fields.datetime('Operation Date'),
        'price_unit_on_quant':
        fields.float('Value'),
        'inventory_value':
        fields.function(_get_inventory_value,
                        string="Inventory Value",
                        type='float',
                        readonly=True),
        'source':
        fields.char('Source'),
        'product_template_id':
        fields.many2one('product.template', 'Product Template', required=True),
        'serial_number':
        fields.char('Serial Number', required=True),
    }

    def init(self, cr):
        tools.drop_view_if_exists(cr, 'stock_history')
        cr.execute("""
            CREATE OR REPLACE VIEW stock_history AS (
              SELECT MIN(id) as id,
                move_id,
                location_id,
                company_id,
                product_id,
                product_categ_id,
                product_template_id,
                SUM(quantity) as quantity,
                date,
                price_unit_on_quant,
                source,
                serial_number
                FROM
                ((SELECT
                    stock_move.id AS id,
                    stock_move.id AS move_id,
                    dest_location.id AS location_id,
                    dest_location.company_id AS company_id,
                    stock_move.product_id AS product_id,
                    product_template.id AS product_template_id,
                    product_template.categ_id AS product_categ_id,
                    quant.qty AS quantity,
                    stock_move.date AS date,
                    quant.cost as price_unit_on_quant,
                    stock_move.origin AS source,
                    stock_production_lot.name AS serial_number
                FROM
                    stock_quant as quant
                JOIN
                    stock_quant_move_rel ON stock_quant_move_rel.quant_id = quant.id
                JOIN
                    stock_move ON stock_move.id = stock_quant_move_rel.move_id
                LEFT JOIN
                    stock_production_lot ON stock_production_lot.id = quant.lot_id
                JOIN
                    stock_location dest_location ON stock_move.location_dest_id = dest_location.id
                JOIN
                    stock_location source_location ON stock_move.location_id = source_location.id
                JOIN
                    product_product ON product_product.id = stock_move.product_id
                JOIN
                    product_template ON product_template.id = product_product.product_tmpl_id
                WHERE quant.qty>0 AND stock_move.state = 'done' AND dest_location.usage in ('internal', 'transit')
                AND (
                    (source_location.company_id is null and dest_location.company_id is not null) or
                    (source_location.company_id is not null and dest_location.company_id is null) or
                    source_location.company_id != dest_location.company_id or
                    source_location.usage not in ('internal', 'transit'))
                ) UNION ALL
                (SELECT
                    (-1) * stock_move.id AS id,
                    stock_move.id AS move_id,
                    source_location.id AS location_id,
                    source_location.company_id AS company_id,
                    stock_move.product_id AS product_id,
                    product_template.id AS product_template_id,
                    product_template.categ_id AS product_categ_id,
                    - quant.qty AS quantity,
                    stock_move.date AS date,
                    quant.cost as price_unit_on_quant,
                    stock_move.origin AS source,
                    stock_production_lot.name AS serial_number
                FROM
                    stock_quant as quant
                JOIN
                    stock_quant_move_rel ON stock_quant_move_rel.quant_id = quant.id
                JOIN
                    stock_move ON stock_move.id = stock_quant_move_rel.move_id
                LEFT JOIN
                    stock_production_lot ON stock_production_lot.id = quant.lot_id
                JOIN
                    stock_location source_location ON stock_move.location_id = source_location.id
                JOIN
                    stock_location dest_location ON stock_move.location_dest_id = dest_location.id
                JOIN
                    product_product ON product_product.id = stock_move.product_id
                JOIN
                    product_template ON product_template.id = product_product.product_tmpl_id
                WHERE quant.qty>0 AND stock_move.state = 'done' AND source_location.usage in ('internal', 'transit')
                AND (
                    (dest_location.company_id is null and source_location.company_id is not null) or
                    (dest_location.company_id is not null and source_location.company_id is null) or
                    dest_location.company_id != source_location.company_id or
                    dest_location.usage not in ('internal', 'transit'))
                ))
                AS foo
                GROUP BY move_id, location_id, company_id, product_id, product_categ_id, date, price_unit_on_quant, source, product_template_id, serial_number
            )""")
예제 #17
0
class sale_order_dates(osv.osv):
    """Add several date fields to Sale Orders, computed or user-entered"""
    _inherit = 'sale.order'

    def _get_effective_date(self, cr, uid, ids, name, arg, context=None):
        """Read the shipping date from the related packings"""
        # TODO: would be better if it returned the date the picking was processed?
        res = {}
        dates_list = []
        for order in self.browse(cr, uid, ids, context=context):
            dates_list = []
            for pick in order.picking_ids:
                dates_list.append(pick.date)
            if dates_list:
                res[order.id] = min(dates_list)
            else:
                res[order.id] = False
        return res

    def _get_commitment_date(self, cr, uid, ids, name, arg, context=None):
        """Compute the commitment date"""
        res = {}
        dates_list = []
        for order in self.browse(cr, uid, ids, context=context):
            dates_list = []
            order_datetime = datetime.strptime(order.date_order,
                                               DEFAULT_SERVER_DATETIME_FORMAT)
            for line in order.order_line:
                if line.state == 'cancel':
                    continue
                dt = order_datetime + timedelta(days=line.customer_lead or 0.0)
                dt_s = dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
                dates_list.append(dt_s)
            if dates_list:
                res[order.id] = min(dates_list)
        return res

    def onchange_requested_date(self,
                                cr,
                                uid,
                                ids,
                                requested_date,
                                commitment_date,
                                context=None):
        """Warn if the requested dates is sooner than the commitment date"""
        if (requested_date and commitment_date
                and requested_date < commitment_date):
            return {
                'warning': {
                    'title':
                    _('Requested date is too soon!'),
                    'message':
                    _("The date requested by the customer is "
                      "sooner than the commitment date. You may be "
                      "unable to honor the customer's request.")
                }
            }
        return {}

    _columns = {
        'commitment_date':
        fields.function(
            _get_commitment_date,
            store=True,
            type='datetime',
            string='Commitment Date',
            help="Date by which the products are sure to be delivered. This is "
            "a date that you can promise to the customer, based on the "
            "Product Lead Times."),
        'requested_date':
        fields.datetime(
            'Requested Date',
            readonly=True,
            states={
                'draft': [('readonly', False)],
                'sent': [('readonly', False)]
            },
            copy=False,
            help="Date by which the customer has requested the items to be "
            "delivered.\n"
            "When this Order gets confirmed, the Delivery Order's "
            "expected date will be computed based on this date and the "
            "Company's Security Delay.\n"
            "Leave this field empty if you want the Delivery Order to be "
            "processed as soon as possible. In that case the expected "
            "date will be computed using the default method: based on "
            "the Product Lead Times and the Company's Security Delay."),
        'effective_date':
        fields.function(
            _get_effective_date,
            type='date',
            store=True,
            string='Effective Date',
            help="Date on which the first Delivery Order was created."),
    }
예제 #18
0
class hr_attendance(osv.osv):
    _name = "hr.attendance"
    _description = "Attendance"

    def _worked_hours_compute(self,
                              cr,
                              uid,
                              ids,
                              fieldnames,
                              args,
                              context=None):
        """For each hr.attendance record of action sign-in: assign 0.
        For each hr.attendance record of action sign-out: assign number of hours since last sign-in.
        """
        res = {}
        for obj in self.browse(cr, uid, ids, context=context):
            if obj.action == 'sign_in':
                res[obj.id] = 0
            elif obj.action == 'sign_out':
                # Get the associated sign-in
                last_signin_id = self.search(
                    cr,
                    uid, [('employee_id', '=', obj.employee_id.id),
                          ('name', '<', obj.name), ('action', '=', 'sign_in')],
                    limit=1,
                    order='name DESC')
                if last_signin_id:
                    last_signin = self.browse(cr,
                                              uid,
                                              last_signin_id,
                                              context=context)[0]

                    # Compute time elapsed between sign-in and sign-out
                    last_signin_datetime = datetime.strptime(
                        last_signin.name, '%Y-%m-%d %H:%M:%S')
                    signout_datetime = datetime.strptime(
                        obj.name, '%Y-%m-%d %H:%M:%S')
                    workedhours_datetime = (signout_datetime -
                                            last_signin_datetime)
                    res[obj.id] = ((workedhours_datetime.seconds) / 60) / 60.0
                else:
                    res[obj.id] = False
        return res

    _columns = {
        'name':
        fields.datetime('Date', required=True, select=1),
        'action':
        fields.selection([('sign_in', 'Sign In'), ('sign_out', 'Sign Out'),
                          ('action', 'Action')],
                         'Action',
                         required=True),
        'action_desc':
        fields.many2one(
            "hr.action.reason",
            "Action Reason",
            domain="[('action_type', '=', action)]",
            help=
            'Specifies the reason for Signing In/Signing Out in case of extra hours.'
        ),
        'employee_id':
        fields.many2one('hr.employee', "Employee", required=True, select=True),
        'department_id':
        fields.many2one('hr.department',
                        "Department",
                        related="employee_id.department_id"),
        'worked_hours':
        fields.function(_worked_hours_compute,
                        type='float',
                        string='Worked Hours',
                        store=True),
    }
    _defaults = {
        'name': lambda *a: time.strftime(
            '%Y-%m-%d %H:%M:%S'
        ),  #please don't remove the lambda, if you remove it then the current time will not change
        'employee_id': _employee_get,
    }

    def _altern_si_so(self, cr, uid, ids, context=None):
        """ Alternance sign_in/sign_out check.
            Previous (if exists) must be of opposite action.
            Next (if exists) must be of opposite action.
        """
        for att in self.browse(cr, uid, ids, context=context):
            # search and browse for first previous and first next records
            prev_att_ids = self.search(
                cr,
                uid, [('employee_id', '=', att.employee_id.id),
                      ('name', '<', att.name),
                      ('action', 'in', ('sign_in', 'sign_out'))],
                limit=1,
                order='name DESC')
            next_add_ids = self.search(
                cr,
                uid, [('employee_id', '=', att.employee_id.id),
                      ('name', '>', att.name),
                      ('action', 'in', ('sign_in', 'sign_out'))],
                limit=1,
                order='name ASC')
            prev_atts = self.browse(cr, uid, prev_att_ids, context=context)
            next_atts = self.browse(cr, uid, next_add_ids, context=context)
            # check for alternance, return False if at least one condition is not satisfied
            if prev_atts and prev_atts[
                    0].action == att.action:  # previous exists and is same action
                return False
            if next_atts and next_atts[
                    0].action == att.action:  # next exists and is same action
                return False
            if (not prev_atts) and (
                    not next_atts
            ) and att.action != 'sign_in':  # first attendance must be sign_in
                return False
        return True

    _constraints = [(
        _altern_si_so,
        'Error ! Sign in (resp. Sign out) must follow Sign out (resp. Sign in)',
        ['action'])]
    _order = 'name desc'
예제 #19
0
class base_action_rule(osv.osv):
    """ Base Action Rules """

    _name = 'base.action.rule'
    _description = 'Action Rules'
    _order = 'sequence'

    _columns = {
        'name':  fields.char('Rule Name', required=True),
        'model_id': fields.many2one('ir.model', 'Related Document Model',
            required=True, domain=[('transient', '=', False)]),
        'model': fields.related('model_id', 'model', type="char", string='Model'),
        'create_date': fields.datetime('Create Date', readonly=1),
        'active': fields.boolean('Active',
            help="When unchecked, the rule is hidden and will not be executed."),
        'sequence': fields.integer('Sequence',
            help="Gives the sequence order when displaying a list of rules."),
        'kind': fields.selection(
            [('on_create', 'On Creation'),
             ('on_write', 'On Update'),
             ('on_create_or_write', 'On Creation & Update'),
             ('on_unlink', 'On Deletion'),
             ('on_change', 'Based on Form Modification'),
             ('on_time', 'Based on Timed Condition')],
            string='When to Run'),
        'trg_date_id': fields.many2one('ir.model.fields', string='Trigger Date',
            help="When should the condition be triggered. If present, will be checked by the scheduler. If empty, will be checked at creation and update.",
            domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]"),
        'trg_date_range': fields.integer('Delay after trigger date',
            help="Delay after the trigger date." \
            "You can put a negative number if you need a delay before the" \
            "trigger date, like sending a reminder 15 minutes before a meeting."),
        'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'),
                                ('day', 'Days'), ('month', 'Months')], 'Delay type'),
        'trg_date_calendar_id': fields.many2one(
            'resource.calendar', 'Use Calendar',
            help='When calculating a day-based timed condition, it is possible to use a calendar to compute the date based on working days.',
            ondelete='set null',
        ),
        'act_user_id': fields.many2one('res.users', 'Set Responsible'),
        'act_followers': fields.many2many("res.partner", string="Add Followers"),
        'server_action_ids': fields.many2many('ir.actions.server', string='Server Actions',
            domain="[('model_id', '=', model_id)]",
            help="Examples: email reminders, call object service, etc."),
        'filter_pre_id': fields.many2one(
            'ir.filters', string='Before Update Filter',
            ondelete='restrict', domain="[('model_id', '=', model_id.model)]",
            help="If present, this condition must be satisfied before the update of the record."),
        'filter_pre_domain': fields.char(string='Before Update Domain', help="If present, this condition must be satisfied before the update of the record."),
        'filter_id': fields.many2one(
            'ir.filters', string='Filter',
            ondelete='restrict', domain="[('model_id', '=', model_id.model)]",
            help="If present, this condition must be satisfied before executing the action rule."),
        'filter_domain': fields.char(string='Domain', help="If present, this condition must be satisfied before executing the action rule."),
        'last_run': fields.datetime('Last Run', readonly=1, copy=False),
        'on_change_fields': fields.char(string="On Change Fields Trigger",
            help="Comma-separated list of field names that triggers the onchange."),
    }

    # which fields have an impact on the registry
    CRITICAL_FIELDS = ['model_id', 'active', 'kind', 'on_change_fields']

    _defaults = {
        'active': True,
        'trg_date_range_type': 'day',
    }

    def onchange_kind(self, cr, uid, ids, kind, context=None):
        clear_fields = []
        if kind in ['on_create', 'on_create_or_write', 'on_unlink']:
            clear_fields = ['filter_pre_id', 'filter_pre_domain', 'trg_date_id', 'trg_date_range', 'trg_date_range_type']
        elif kind in ['on_write', 'on_create_or_write']:
            clear_fields = ['trg_date_id', 'trg_date_range', 'trg_date_range_type']
        elif kind == 'on_time':
            clear_fields = ['filter_pre_id', 'filter_pre_domain']
        return {'value': dict.fromkeys(clear_fields, False)}

    def onchange_filter_pre_id(self, cr, uid, ids, filter_pre_id, context=None):
        ir_filter = self.pool['ir.filters'].browse(cr, uid, filter_pre_id, context=context)
        return {'value': {'filter_pre_domain': ir_filter.domain}}

    def onchange_filter_id(self, cr, uid, ids, filter_id, context=None):
        ir_filter = self.pool['ir.filters'].browse(cr, uid, filter_id, context=context)
        return {'value': {'filter_domain': ir_filter.domain}}

    @ecore.api.model
    def _get_actions(self, records, kinds):
        """ Return the actions of the given kinds for records' model. The
        returned actions' context contain an object to manage processing.
        """
        if '__action_done' not in self._context:
            self = self.with_context(__action_done={})
        domain = [('model', '=', records._name), ('kind', 'in', kinds)]
        actions = self.with_context(active_test=True).search(domain)
        return actions.with_env(self.env)

    @ecore.api.model
    def _get_eval_context(self):
        """ Prepare the context used when evaluating python code
        :returns: dict -- evaluation context given to (safe_)eval """
        return {
            'datetime': DT,
            'dateutil': dateutil,
            'time': time,
            'uid': self.env.uid,
            'user': self.env.user,
        }

    @ecore.api.model
    def _filter_pre(self, records):
        """ Filter the records that satisfy the precondition of action ``self``. """
        if self.filter_pre_id and records:
            eval_context = self._get_eval_context()
            domain = [('id', 'in', records.ids)] + eval(self.filter_pre_id.domain, eval_context)
            ctx = eval(self.filter_pre_id.context)
            return records.with_context(**ctx).search(domain).with_env(records.env)
        elif self.filter_pre_domain and records:
            eval_context = self._get_eval_context()
            domain = [('id', 'in', records.ids)] + eval(self.filter_pre_domain, eval_context)
            return records.search(domain)
        else:
            return records

    @ecore.api.model
    def _filter_post(self, records):
        """ Filter the records that satisfy the postcondition of action ``self``. """
        if self.filter_id and records:
            eval_context = self._get_eval_context()
            domain = [('id', 'in', records.ids)] + eval(self.filter_id.domain, eval_context)
            ctx = eval(self.filter_id.context)
            return records.with_context(**ctx).search(domain).with_env(records.env)
        elif self.filter_domain and records:
            eval_context = self._get_eval_context()
            domain = [('id', 'in', records.ids)] + eval(self.filter_domain, eval_context)
            return records.search(domain)
        else:
            return records

    @ecore.api.multi
    def _process(self, records):
        """ Process action ``self`` on the ``records`` that have not been done yet. """
        # filter out the records on which self has already been done, then mark
        # remaining records as done (to avoid recursive processing)
        action_done = self._context['__action_done']
        records -= action_done.setdefault(self, records.browse())
        if not records:
            return
        action_done[self] |= records

        # modify records
        values = {}
        if 'date_action_last' in records._fields:
            values['date_action_last'] = ecore.fields.Datetime.now()
        if self.act_user_id and 'user_id' in records._fields:
            values['user_id'] = self.act_user_id.id
        if values:
            records.write(values)

        # subscribe followers
        if self.act_followers and hasattr(records, 'message_subscribe'):
            records.message_subscribe(self.act_followers.ids)

        # execute server actions
        if self.server_action_ids:
            for record in records:
                ctx = {'active_model': record._name, 'active_ids': record.ids, 'active_id': record.id}
                self.server_action_ids.with_context(**ctx).run()

    def _register_hook(self, cr):
        """ Patch models that should trigger action rules based on creation,
        modification, deletion of records and form onchanges.
        """
        #
        # Note: the patched methods must be defined inside another function,
        # otherwise their closure may be wrong. For instance, the function
        # create refers to the outer variable 'create', which you expect to be
        # bound to create itself. But that expectation is wrong if create is
        # defined inside a loop; in that case, the variable 'create' is bound to
        # the last function defined by the loop.
        #

        def make_create():
            """ Instanciate a create method that processes action rules. """
            @ecore.api.model
            def create(self, vals):
                # retrieve the action rules to possibly execute
                actions = self.env['base.action.rule']._get_actions(self, ['on_create', 'on_create_or_write'])

                # call original method
                record = create.origin(self.with_env(actions.env), vals)

                # check postconditions, and execute actions on the records that satisfy them
                for action in actions.with_context(old_values=None):
                    action._process(action._filter_post(record))

                return record.with_env(self.env)

            return create

        def make_write():
            """ Instanciate a _write method that processes action rules. """
            #
            # Note: we patch method _write() instead of write() in order to
            # catch updates made by field recomputations.
            #
            @ecore.api.multi
            def _write(self, vals):
                # retrieve the action rules to possibly execute
                actions = self.env['base.action.rule']._get_actions(self, ['on_write', 'on_create_or_write'])
                records = self.with_env(actions.env)

                # check preconditions on records
                pre = {action: action._filter_pre(records) for action in actions}

                # read old values before the update
                old_values = {
                    old_vals.pop('id'): old_vals
                    for old_vals in records.read(list(vals))
                }

                # call original method
                _write.origin(records, vals)

                # check postconditions, and execute actions on the records that satisfy them
                for action in actions.with_context(old_values=old_values):
                    action._process(action._filter_post(pre[action]))
                return True

            return _write

        def make_unlink():
            """ Instanciate an unlink method that processes action rules. """
            @ecore.api.multi
            def unlink(self, **kwargs):
                # retrieve the action rules to possibly execute
                actions = self.env['base.action.rule']._get_actions(self, ['on_unlink'])
                records = self.with_env(actions.env)

                # check conditions, and execute actions on the records that satisfy them
                for action in actions:
                    action._process(action._filter_post(pre[action]))

                # call original method
                return unlink.origin(self, **kwargs)

            return unlink

        def make_onchange(action_rule_id):
            """ Instanciate an onchange method for the given action rule. """
            def base_action_rule_onchange(self):
                action_rule = self.env['base.action.rule'].browse(action_rule_id)
                server_actions = action_rule.server_action_ids.with_context(active_model=self._name, onchange_self=self)
                result = {}
                for server_action in server_actions:
                    res = server_action.run()
                    if res and 'value' in res:
                        res['value'].pop('id', None)
                        self.update(self._convert_to_cache(res['value'], validate=False))
                    if res and 'domain' in res:
                        result.setdefault('domain', {}).update(res['domain'])
                    if res and 'warning' in res:
                        result['warning'] = res['warning']
                return result

            return base_action_rule_onchange

        patched_models = defaultdict(set)
        def patch(model, name, method):
            """ Patch method `name` on `model`, unless it has been patched already. """
            if model not in patched_models[name]:
                patched_models[name].add(model)
                model._patch_method(name, method)

        # retrieve all actions, and patch their corresponding model
        ids = self.search(cr, SUPERUSER_ID, [])
        for action_rule in self.browse(cr, SUPERUSER_ID, ids):
            model = action_rule.model_id.model
            model_obj = self.pool.get(model)
            if not model_obj:
                continue

            if action_rule.kind == 'on_create':
                patch(model_obj, 'create', make_create())

            elif action_rule.kind == 'on_create_or_write':
                patch(model_obj, 'create', make_create())
                patch(model_obj, '_write', make_write())

            elif action_rule.kind == 'on_write':
                patch(model_obj, '_write', make_write())

            elif action_rule.kind == 'on_unlink':
                patch(model_obj, 'unlink', make_unlink())

            elif action_rule.kind == 'on_change':
                # register an onchange method for the action_rule
                method = make_onchange(action_rule.id)
                for field_name in action_rule.on_change_fields.split(","):
                    field_name = field_name.strip()
                    model_obj._onchange_methods[field_name].append(method)

    def _update_cron(self, cr, uid, context=None):
        """ Activate the cron job depending on whether there exists action rules
        based on time conditions. """
        try:
            cron = self.pool['ir.model.data'].get_object(
                cr, uid, 'base_action_rule', 'ir_cron_crm_action', context=context)
        except ValueError:
            return False

        return cron.toggle(model=self._name, domain=[('kind', '=', 'on_time')])

    def _update_registry(self, cr, uid, context=None):
        """ Update the registry after a modification on action rules. """
        if self.pool.ready:
            # for the sake of simplicity, simply force the registry to reload
            cr.commit()
            ecore.api.Environment.reset()
            RegistryManager.new(cr.dbname)
            RegistryManager.signal_registry_change(cr.dbname)

    def create(self, cr, uid, vals, context=None):
        res_id = super(base_action_rule, self).create(cr, uid, vals, context=context)
        self._update_cron(cr, uid, context=context)
        self._update_registry(cr, uid, context=context)
        return res_id

    def write(self, cr, uid, ids, vals, context=None):
        super(base_action_rule, self).write(cr, uid, ids, vals, context=context)
        if set(vals) & set(self.CRITICAL_FIELDS):
            self._update_cron(cr, uid, context=context)
            self._update_registry(cr, uid, context=context)
        return True

    def unlink(self, cr, uid, ids, context=None):
        res = super(base_action_rule, self).unlink(cr, uid, ids, context=context)
        self._update_cron(cr, uid, context=context)
        self._update_registry(cr, uid, context=context)
        return res

    def onchange_model_id(self, cr, uid, ids, model_id, context=None):
        data = {'model': False, 'filter_pre_id': False, 'filter_id': False}
        if model_id:
            model = self.pool.get('ir.model').browse(cr, uid, model_id, context=context)
            data.update({'model': model.model})
        return {'value': data}

    def _check_delay(self, cr, uid, action, record, record_dt, context=None):
        if action.trg_date_calendar_id and action.trg_date_range_type == 'day':
            start_dt = get_datetime(record_dt)
            action_dt = self.pool['resource.calendar'].schedule_days_get_date(
                cr, uid, action.trg_date_calendar_id.id, action.trg_date_range,
                day_date=start_dt, compute_leaves=True, context=context
            )
        else:
            delay = DATE_RANGE_FUNCTION[action.trg_date_range_type](action.trg_date_range)
            action_dt = get_datetime(record_dt) + delay
        return action_dt

    def _check(self, cr, uid, automatic=False, use_new_cursor=False, context=None):
        """ This Function is called by scheduler. """
        context = context or {}
        # retrieve all the action rules to run based on a timed condition
        action_dom = [('kind', '=', 'on_time')]
        action_ids = self.search(cr, uid, action_dom, context=dict(context, active_test=True))
        eval_context = self._get_eval_context(cr, uid, context=context)
        for action in self.browse(cr, uid, action_ids, context=context):
            now = datetime.now()
            if action.last_run:
                last_run = get_datetime(action.last_run)
            else:
                last_run = datetime.utcfromtimestamp(0)
            # retrieve all the records that satisfy the action's condition
            model = self.pool[action.model_id.model]
            domain = []
            ctx = dict(context)
            if action.filter_domain is not False:
                domain = eval(action.filter_domain, eval_context)
            elif action.filter_id:
                domain = eval(action.filter_id.domain, eval_context)
                ctx.update(eval(action.filter_id.context))
                if 'lang' not in ctx:
                    # Filters might be language-sensitive, attempt to reuse creator lang
                    # as we are usually running this as super-user in background
                    [filter_meta] = action.filter_id.get_metadata()
                    user_id = filter_meta['write_uid'] and filter_meta['write_uid'][0] or \
                                    filter_meta['create_uid'][0]
                    ctx['lang'] = self.pool['res.users'].browse(cr, uid, user_id).lang
            record_ids = model.search(cr, uid, domain, context=ctx)

            # determine when action should occur for the records
            date_field = action.trg_date_id.name
            if date_field == 'date_action_last' and 'create_date' in model._fields:
                get_record_dt = lambda record: record[date_field] or record.create_date
            else:
                get_record_dt = lambda record: record[date_field]

            # process action on the records that should be executed
            for record in model.browse(cr, uid, record_ids, context=context):
                record_dt = get_record_dt(record)
                if not record_dt:
                    continue
                action_dt = self._check_delay(cr, uid, action, record, record_dt, context=context)
                if last_run <= action_dt < now:
                    try:
                        context = dict(context or {}, action=True)
                        self._process(cr, uid, action, [record.id], context=context)
                    except Exception:
                        import traceback
                        _logger.error(traceback.format_exc())

            action.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)})

            if automatic:
                # auto-commit for batch processing
                cr.commit()
예제 #20
0
class hr_job(osv.Model):
    def _get_nbr_employees(self, cr, uid, ids, name, args, context=None):
        res = {}
        for job in self.browse(cr, uid, ids, context=context):
            nb_employees = len(job.employee_ids or [])
            res[job.id] = {
                'no_of_employee': nb_employees,
                'expected_employees': nb_employees + job.no_of_recruitment,
            }
        return res

    def _get_job_position(self, cr, uid, ids, context=None):
        res = []
        for employee in self.pool.get('hr.employee').browse(cr,
                                                            uid,
                                                            ids,
                                                            context=context):
            if employee.job_id:
                res.append(employee.job_id.id)
        return res

    _name = "hr.job"
    _description = "Job Position"
    _inherit = ['mail.thread']
    _columns = {
        'name':
        fields.char('Job Name', required=True, select=True, translate=True),
        'expected_employees':
        fields.function(
            _get_nbr_employees,
            string='Total Forecasted Employees',
            help=
            'Expected number of employees for this job position after new recruitment.',
            store={
                'hr.job': (lambda self, cr, uid, ids, c=None: ids,
                           ['no_of_recruitment'], 10),
                'hr.employee': (_get_job_position, ['job_id'], 10),
            },
            type='integer',
            multi='_get_nbr_employees'),
        'no_of_employee':
        fields.function(
            _get_nbr_employees,
            string="Current Number of Employees",
            help='Number of employees currently occupying this job position.',
            store={
                'hr.employee': (_get_job_position, ['job_id'], 10),
            },
            type='integer',
            multi='_get_nbr_employees'),
        'no_of_recruitment':
        fields.integer('Expected New Employees',
                       copy=False,
                       help='Number of new employees you expect to recruit.'),
        'no_of_hired_employee':
        fields.integer(
            'Hired Employees',
            copy=False,
            help=
            'Number of hired employees for this job position during recruitment phase.'
        ),
        'employee_ids':
        fields.one2many('hr.employee',
                        'job_id',
                        'Employees',
                        groups='base.group_user'),
        'description':
        fields.text('Job Description'),
        'requirements':
        fields.text('Requirements'),
        'department_id':
        fields.many2one('hr.department', 'Department'),
        'company_id':
        fields.many2one('res.company', 'Company'),
        'state':
        fields.selection(
            [('recruit', 'Recruitment in Progress'),
             ('open', 'Recruitment Closed')],
            string='Status',
            readonly=True,
            required=True,
            track_visibility='always',
            copy=False,
            help=
            "Set whether the recruitment process is open or closed for this job position."
        ),
        'write_date':
        fields.datetime('Update Date', readonly=True),
    }

    _defaults = {
        'company_id':
        lambda self, cr, uid, ctx=None: self.pool.get('res.company').
        _company_default_get(cr, uid, 'hr.job', context=ctx),
        'state':
        'recruit',
        'no_of_recruitment':
        1,
    }

    _sql_constraints = [
        ('name_company_uniq', 'unique(name, company_id, department_id)',
         'The name of the job position must be unique per department in company!'
         ),
    ]

    def set_recruit(self, cr, uid, ids, context=None):
        for job in self.browse(cr, uid, ids, context=context):
            no_of_recruitment = job.no_of_recruitment == 0 and 1 or job.no_of_recruitment
            self.write(cr,
                       uid, [job.id], {
                           'state': 'recruit',
                           'no_of_recruitment': no_of_recruitment
                       },
                       context=context)
        return True

    def set_open(self, cr, uid, ids, context=None):
        self.write(cr,
                   uid,
                   ids, {
                       'state': 'open',
                       'no_of_recruitment': 0,
                       'no_of_hired_employee': 0
                   },
                   context=context)
        return True

    # TDE note: done in new api, because called with new api -> context is a
    # frozendict -> error when tryign to manipulate it
    @api.model
    def create(self, values):
        return super(
            hr_job,
            self.with_context(mail_create_nosubscribe=True)).create(values)

    def copy(self, cr, uid, id, default=None, context=None):
        if default is None:
            default = {}
        if 'name' not in default:
            job = self.browse(cr, uid, id, context=context)
            default['name'] = _("%s (copy)") % (job.name)
        return super(hr_job, self).copy(cr,
                                        uid,
                                        id,
                                        default=default,
                                        context=context)

    # ----------------------------------------
    # Compatibility methods
    # ----------------------------------------
    _no_of_employee = _get_nbr_employees  # v7 compatibility
    job_open = set_open  # v7 compatibility
    job_recruitment = set_recruit  # v7 compatibility
예제 #21
0
파일: paypal.py 프로젝트: LiberTang0/5
class AcquirerPaypal(osv.Model):
    _inherit = 'payment.acquirer'

    def _get_paypal_urls(self, cr, uid, environment, context=None):
        """ Paypal URLS """
        if environment == 'prod':
            return {
                'paypal_form_url': 'https://www.paypal.com/cgi-bin/webscr',
                'paypal_rest_url': 'https://api.paypal.com/v1/oauth2/token',
            }
        else:
            return {
                'paypal_form_url': 'https://www.sandbox.paypal.com/cgi-bin/webscr',
                'paypal_rest_url': 'https://api.sandbox.paypal.com/v1/oauth2/token',
            }

    def _get_providers(self, cr, uid, context=None):
        providers = super(AcquirerPaypal, self)._get_providers(cr, uid, context=context)
        providers.append(['paypal', 'Paypal'])
        return providers

    _columns = {
        'paypal_email_account': fields.char('Paypal Email ID', required_if_provider='paypal'),
        'paypal_seller_account': fields.char(
            'Paypal Merchant ID',
            help='The Merchant ID is used to ensure communications coming from Paypal are valid and secured.'),
        'paypal_use_ipn': fields.boolean('Use IPN', help='Paypal Instant Payment Notification'),
        # Server 2 server
        'paypal_api_enabled': fields.boolean('Use Rest API'),
        'paypal_api_username': fields.char('Rest API Username'),
        'paypal_api_password': fields.char('Rest API Password'),
        'paypal_api_access_token': fields.char('Access Token'),
        'paypal_api_access_token_validity': fields.datetime('Access Token Validity'),
    }

    _defaults = {
        'paypal_use_ipn': True,
        'fees_active': False,
        'fees_dom_fixed': 0.35,
        'fees_dom_var': 3.4,
        'fees_int_fixed': 0.35,
        'fees_int_var': 3.9,
        'paypal_api_enabled': False,
    }

    def _migrate_paypal_account(self, cr, uid, context=None):
        """ COMPLETE ME """
        cr.execute('SELECT id, paypal_account FROM res_company')
        res = cr.fetchall()
        for (company_id, company_paypal_account) in res:
            if company_paypal_account:
                company_paypal_ids = self.search(cr, uid, [('company_id', '=', company_id), ('provider', '=', 'paypal')], limit=1, context=context)
                if company_paypal_ids:
                    self.write(cr, uid, company_paypal_ids, {'paypal_email_account': company_paypal_account}, context=context)
                else:
                    paypal_view = self.pool['ir.model.data'].get_object(cr, uid, 'payment_paypal', 'paypal_acquirer_button')
                    self.create(cr, uid, {
                        'name': 'Paypal',
                        'provider': 'paypal',
                        'paypal_email_account': company_paypal_account,
                        'view_template_id': paypal_view.id,
                    }, context=context)
        return True

    def paypal_compute_fees(self, cr, uid, id, amount, currency_id, country_id, context=None):
        """ Compute paypal fees.

            :param float amount: the amount to pay
            :param integer country_id: an ID of a res.country, or None. This is
                                       the customer's country, to be compared to
                                       the acquirer company country.
            :return float fees: computed fees
        """
        acquirer = self.browse(cr, uid, id, context=context)
        if not acquirer.fees_active:
            return 0.0
        country = self.pool['res.country'].browse(cr, uid, country_id, context=context)
        if country and acquirer.company_id.country_id.id == country.id:
            percentage = acquirer.fees_dom_var
            fixed = acquirer.fees_dom_fixed
        else:
            percentage = acquirer.fees_int_var
            fixed = acquirer.fees_int_fixed
        fees = (percentage / 100.0 * amount + fixed ) / (1 - percentage / 100.0)
        return fees

    def paypal_form_generate_values(self, cr, uid, id, values, context=None):
        base_url = self.pool['ir.config_parameter'].get_param(cr, SUPERUSER_ID, 'web.base.url')
        acquirer = self.browse(cr, uid, id, context=context)

        paypal_tx_values = dict(values)
        paypal_tx_values.update({
            'cmd': '_xclick',
            'business': acquirer.paypal_email_account,
            'item_name': '%s: %s' % (acquirer.company_id.name, values['reference']),
            'item_number': values['reference'],
            'amount': values['amount'],
            'currency_code': values['currency'] and values['currency'].name or '',
            'address1': values.get('partner_address'),
            'city': values.get('partner_city'),
            'country': values.get('partner_country') and values.get('partner_country').name or '',
            'state': values.get('partner_state') and values.get('partner_state').name or '',
            'email': values.get('partner_email'),
            'zip_code': values.get('partner_zip'),
            'first_name': values.get('partner_first_name'),
            'last_name': values.get('partner_last_name'),
            'paypal_return': '%s' % urlparse.urljoin(base_url, PaypalController._return_url),
            'notify_url': '%s' % urlparse.urljoin(base_url, PaypalController._notify_url),
            'cancel_return': '%s' % urlparse.urljoin(base_url, PaypalController._cancel_url),
            'handling': '%.2f' % paypal_tx_values.pop('fees', 0.0) if acquirer.fees_active else False,
            'custom': json.dumps({'return_url': '%s' % paypal_tx_values.pop('return_url')}) if paypal_tx_values.get('return_url') else False,
        })
        return paypal_tx_values

    def paypal_get_form_action_url(self, cr, uid, id, context=None):
        acquirer = self.browse(cr, uid, id, context=context)
        return self._get_paypal_urls(cr, uid, acquirer.environment, context=context)['paypal_form_url']

    def _paypal_s2s_get_access_token(self, cr, uid, ids, context=None):
        """
        Note: see # see http://stackoverflow.com/questions/2407126/python-urllib2-basic-auth-problem
        for explanation why we use Authorization header instead of urllib2
        password manager
        """
        res = dict.fromkeys(ids, False)
        parameters = werkzeug.url_encode({'grant_type': 'client_credentials'})

        for acquirer in self.browse(cr, uid, ids, context=context):
            tx_url = self._get_paypal_urls(cr, uid, acquirer.environment)['paypal_rest_url']
            request = urllib2.Request(tx_url, parameters)

            # add other headers (https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/)
            request.add_header('Accept', 'application/json')
            request.add_header('Accept-Language', 'en_US')

            # add authorization header
            base64string = base64.encodestring('%s:%s' % (
                acquirer.paypal_api_username,
                acquirer.paypal_api_password)
            ).replace('\n', '')
            request.add_header("Authorization", "Basic %s" % base64string)

            request = urllib2.urlopen(request)
            result = request.read()
            res[acquirer.id] = json.loads(result).get('access_token')
            request.close()
        return res
예제 #22
0
class crm_claim_report(osv.osv):
    """ CRM Claim Report"""

    _name = "crm.claim.report"
    _auto = False
    _description = "CRM Claim Report"

    _columns = {
        'user_id':
        fields.many2one('res.users', 'User', readonly=True),
        'team_id':
        fields.many2one('crm.team',
                        'Team',
                        oldname='section_id',
                        readonly=True),
        'nbr':
        fields.integer(
            '# of Claims',
            readonly=True),  # TDE FIXME master: rename into nbr_claims
        'company_id':
        fields.many2one('res.company', 'Company', readonly=True),
        'create_date':
        fields.datetime('Create Date', readonly=True, select=True),
        'claim_date':
        fields.datetime('Claim Date', readonly=True),
        'delay_close':
        fields.float('Delay to close',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg",
                     help="Number of Days to close the case"),
        'stage_id':
        fields.many2one('crm.claim.stage',
                        'Stage',
                        readonly=True,
                        domain="[('team_ids','=',team_id)]"),
        'categ_id':
        fields.many2one('crm.claim.category', 'Category', readonly=True),
        'partner_id':
        fields.many2one('res.partner', 'Partner', readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company', readonly=True),
        'priority':
        fields.selection(AVAILABLE_PRIORITIES, 'Priority'),
        'type_action':
        fields.selection([('correction', 'Corrective Action'),
                          ('prevention', 'Preventive Action')], 'Action Type'),
        'date_closed':
        fields.datetime('Close Date', readonly=True, select=True),
        'date_deadline':
        fields.date('Deadline', readonly=True, select=True),
        'delay_expected':
        fields.float('Overpassed Deadline',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg"),
        'email':
        fields.integer('# Emails', size=128, readonly=True),
        'subject':
        fields.char('Claim Subject', readonly=True)
    }

    def init(self, cr):
        """ Display Number of cases And Team Name
        @param cr: the current row, from the database cursor,
         """

        tools.drop_view_if_exists(cr, 'crm_claim_report')
        cr.execute("""
            create or replace view crm_claim_report as (
                select
                    min(c.id) as id,
                    c.date as claim_date,
                    c.date_closed as date_closed,
                    c.date_deadline as date_deadline,
                    c.user_id,
                    c.stage_id,
                    c.team_id,
                    c.partner_id,
                    c.company_id,
                    c.categ_id,
                    c.name as subject,
                    count(*) as nbr,
                    c.priority as priority,
                    c.type_action as type_action,
                    c.create_date as create_date,
                    avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as  delay_close,
                    (SELECT count(id) FROM mail_message WHERE model='crm.claim' AND res_id=c.id) AS email,
                    extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24) as  delay_expected
                from
                    crm_claim c
                group by c.date,\
                        c.user_id,c.team_id, c.stage_id,\
                        c.categ_id,c.partner_id,c.company_id,c.create_date,
                        c.priority,c.type_action,c.date_deadline,c.date_closed,c.id
            )""")
예제 #23
0
class PaymentTransaction(osv.Model):
    """ Transaction Model. Each specific acquirer can extend the model by adding
    its own fields.

    Methods that can be added in an acquirer-specific implementation:

     - ``<name>_create``: method receiving values used when creating a new
       transaction and that returns a dictionary that will update those values.
       This method can be used to tweak some transaction values.

    Methods defined for convention, depending on your controllers:

     - ``<name>_form_feedback(self, cr, uid, data, context=None)``: method that
       handles the data coming from the acquirer after the transaction. It will
       generally receives data posted by the acquirer after the transaction.
    """
    _name = 'payment.transaction'
    _description = 'Payment Transaction'
    _order = 'id desc'
    _rec_name = 'reference'

    def _lang_get(self, cr, uid, context=None):
        lang_ids = self.pool['res.lang'].search(cr, uid, [], context=context)
        languages = self.pool['res.lang'].browse(cr, uid, lang_ids, context=context)
        return [(language.code, language.name) for language in languages]

    def _default_partner_country_id(self, cr, uid, context=None):
        comp = self.pool['res.company'].browse(cr, uid, context.get('company_id', 1), context=context)
        return comp.country_id.id

    _columns = {
        'create_date': fields.datetime('Creation Date', readonly=True),
        'date_validate': fields.datetime('Validation Date'),
        'acquirer_id': fields.many2one(
            'payment.acquirer', 'Acquirer',
            required=True,
        ),
        'type': fields.selection(
            [('server2server', 'Server To Server'), ('form', 'Form'), ('form_save', 'Form with credentials storage')],
            string='Type', required=True),
        'state': fields.selection(
            [('draft', 'Draft'), ('pending', 'Pending'),
             ('done', 'Done'), ('error', 'Error'),
             ('cancel', 'Canceled')
             ], 'Status', required=True,
            track_visibility='onchange', copy=False),
        'state_message': fields.text('Message',
                                     help='Field used to store error and/or validation messages for information'),
        # payment
        'amount': fields.float('Amount', required=True,
                               digits=(16, 2),
                               track_visibility='always',
                               help='Amount'),
        'fees': fields.float('Fees',
                             digits=(16, 2),
                             track_visibility='always',
                             help='Fees amount; set by the system because depends on the acquirer'),
        'currency_id': fields.many2one('res.currency', 'Currency', required=True),
        'reference': fields.char('Reference', required=True, help='Internal reference of the TX'),
        'acquirer_reference': fields.char('Acquirer Reference',
                                          help='Reference of the TX as stored in the acquirer database'),
        # duplicate partner / transaction data to store the values at transaction time
        'partner_id': fields.many2one('res.partner', 'Partner', track_visibility='onchange',),
        'partner_name': fields.char('Partner Name'),
        'partner_lang': fields.selection(_lang_get, 'Language'),
        'partner_email': fields.char('Email'),
        'partner_zip': fields.char('Zip'),
        'partner_address': fields.char('Address'),
        'partner_city': fields.char('City'),
        'partner_country_id': fields.many2one('res.country', 'Country', required=True),
        'partner_phone': fields.char('Phone'),
        'html_3ds': fields.char('3D Secure HTML'),

        'callback_eval': fields.char('S2S Callback', help="""\
            Will be safe_eval with `self` being the current transaction. i.e.:
                self.env['my.model'].payment_validated(self)""", oldname="s2s_cb_eval"),
        'payment_method_id': fields.many2one('payment.method', 'Payment Method', domain="[('acquirer_id', '=', acquirer_id)]"),
    }

    def _check_reference(self, cr, uid, ids, context=None):
        transaction = self.browse(cr, uid, ids[0], context=context)
        if transaction.state not in ['cancel', 'error']:
            if self.search(cr, uid, [('reference', '=', transaction.reference), ('id', '!=', transaction.id)], context=context, count=True):
                return False
        return True

    _constraints = [
        (_check_reference, 'The payment transaction reference must be unique!', ['reference', 'state']),
    ]

    _defaults = {
        'type': 'form',
        'state': 'draft',
        'partner_lang': 'en_US',
        'partner_country_id': _default_partner_country_id,
        'reference': lambda s, c, u, ctx=None: s.pool['ir.sequence'].next_by_code(c, u, 'payment.transaction', context=ctx),
    }

    def create(self, cr, uid, values, context=None):
        Acquirer = self.pool['payment.acquirer']

        if values.get('partner_id'):  # @TDENOTE: not sure
            values.update(self.on_change_partner_id(cr, uid, None, values.get('partner_id'), context=context)['value'])

        # call custom create method if defined (i.e. ogone_create for ogone)
        if values.get('acquirer_id'):
            acquirer = self.pool['payment.acquirer'].browse(cr, uid, values.get('acquirer_id'), context=context)

            # compute fees
            custom_method_name = '%s_compute_fees' % acquirer.provider
            if hasattr(Acquirer, custom_method_name):
                fees = getattr(Acquirer, custom_method_name)(
                    cr, uid, acquirer.id, values.get('amount', 0.0), values.get('currency_id'), values.get('partner_country_id'), context=None)
                values['fees'] = float_round(fees, 2)

            # custom create
            custom_method_name = '%s_create' % acquirer.provider
            if hasattr(self, custom_method_name):
                values.update(getattr(self, custom_method_name)(cr, uid, values, context=context))

        # Default value of reference is
        tx_id = super(PaymentTransaction, self).create(cr, uid, values, context=context)
        if not values.get('reference'):
            self.write(cr, uid, [tx_id], {'reference': str(tx_id)}, context=context)
        return tx_id

    def write(self, cr, uid, ids, values, context=None):
        Acquirer = self.pool['payment.acquirer']
        if ('acquirer_id' in values or 'amount' in values) and 'fees' not in values:
            # The acquirer or the amount has changed, and the fees are not explicitely forced. Fees must be recomputed.
            if isinstance(ids, (int, long)):
                ids = [ids]
            for txn_id in ids:
                vals = dict(values)
                vals['fees'] = 0.0
                transaction = self.browse(cr, uid, txn_id, context=context)
                if 'acquirer_id' in values:
                    acquirer = Acquirer.browse(cr, uid, values['acquirer_id'], context=context) if values['acquirer_id'] else None
                else:
                    acquirer = transaction.acquirer_id
                if acquirer:
                    custom_method_name = '%s_compute_fees' % acquirer.provider
                    if hasattr(Acquirer, custom_method_name):
                        amount = (values['amount'] if 'amount' in values else transaction.amount) or 0.0
                        currency_id = values.get('currency_id') or transaction.currency_id.id
                        country_id = values.get('partner_country_id') or transaction.partner_country_id.id
                        fees = getattr(Acquirer, custom_method_name)(cr, uid, acquirer.id, amount, currency_id, country_id, context=None)
                        vals['fees'] = float_round(fees, 2)
                res = super(PaymentTransaction, self).write(cr, uid, txn_id, vals, context=context)
            return res
        return super(PaymentTransaction, self).write(cr, uid, ids, values, context=context)

    def on_change_partner_id(self, cr, uid, ids, partner_id, context=None):
        partner = None
        if partner_id:
            partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context)
            return {'value': {
                'partner_name': partner and partner.name or False,
                'partner_lang': partner and partner.lang or 'en_US',
                'partner_email': partner and partner.email or False,
                'partner_zip': partner and partner.zip or False,
                'partner_address': _partner_format_address(partner and partner.street or '', partner and partner.street2 or ''),
                'partner_city': partner and partner.city or False,
                'partner_country_id': partner and partner.country_id.id or self._default_partner_country_id(cr, uid, context=context),
                'partner_phone': partner and partner.phone or False,
            }}
        return {}

    def get_next_reference(self, cr, uid, reference, context=None):
        ref_suffix = 1
        init_ref = reference
        while self.pool['payment.transaction'].search_count(cr, uid, [('reference', '=', reference)], context=context):
            reference = init_ref + '-' + str(ref_suffix)
            ref_suffix += 1
        return reference

    # --------------------------------------------------
    # FORM RELATED METHODS
    # --------------------------------------------------

    def render(self, cr, uid, id, context=None):
        tx = self.browse(cr, uid, id, context=context)
        values = {
            'reference': tx.reference,
            'amount': tx.amount,
            'currency_id': tx.currency_id.id,
            'currency': tx.currency_id,
            'partner': tx.partner_id,
            'partner_name': tx.partner_name,
            'partner_lang': tx.partner_lang,
            'partner_email': tx.partner_email,
            'partner_zip': tx.partner_zip,
            'partner_address': tx.partner_address,
            'partner_city': tx.partner_city,
            'partner_country_id': tx.partner_country_id.id,
            'partner_country': tx.partner_country_id,
            'partner_phone': tx.partner_phone,
            'partner_state': None,
        }
        return tx.acquirer_id.render(None, None, None, values=values)

    def form_feedback(self, cr, uid, data, acquirer_name, context=None):
        invalid_parameters, tx = None, None

        tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name
        if hasattr(self, tx_find_method_name):
            tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context)

        invalid_param_method_name = '_%s_form_get_invalid_parameters' % acquirer_name
        if hasattr(self, invalid_param_method_name):
            invalid_parameters = getattr(self, invalid_param_method_name)(cr, uid, tx, data, context=context)

        if invalid_parameters:
            _error_message = '%s: incorrect tx data:\n' % (acquirer_name)
            for item in invalid_parameters:
                _error_message += '\t%s: received %s instead of %s\n' % (item[0], item[1], item[2])
            _logger.error(_error_message)
            return False

        feedback_method_name = '_%s_form_validate' % acquirer_name
        if hasattr(self, feedback_method_name):
            return getattr(self, feedback_method_name)(cr, uid, tx, data, context=context)

        return True

    # --------------------------------------------------
    # SERVER2SERVER RELATED METHODS
    # --------------------------------------------------
    def s2s_create(self, cr, uid, values, cc_values, context=None):
        tx_id, tx_result = self.s2s_send(cr, uid, values, cc_values, context=context)
        self.s2s_feedback(cr, uid, tx_id, tx_result, context=context)
        return tx_id

    def s2s_do_transaction(self, cr, uid, id, context=None, **kwargs):
        tx = self.browse(cr, uid, id, context=context)
        custom_method_name = '%s_s2s_do_transaction' % tx.acquirer_id.provider
        if hasattr(self, custom_method_name):
            return getattr(self, custom_method_name)(cr, uid, id, context=context, **kwargs)

    def s2s_get_tx_status(self, cr, uid, tx_id, context=None):
        """ Get the tx status. """
        tx = self.browse(cr, uid, tx_id, context=context)

        invalid_param_method_name = '_%s_s2s_get_tx_status' % tx.acquirer_id.provider
        if hasattr(self, invalid_param_method_name):
            return getattr(self, invalid_param_method_name)(cr, uid, tx, context=context)

        return True
예제 #24
0
class mrp_operations_operation(osv.osv):
    _name = "mrp_operations.operation"

    def _order_date_search_production(self, cr, uid, ids, context=None):
        """ Finds operations for a production order.
        @return: List of ids
        """
        operation_ids = self.pool.get('mrp_operations.operation').search(
            cr, uid, [('production_id', '=', ids[0])], context=context)
        return operation_ids

    def _get_order_date(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates planned date for an operation.
        @return: Dictionary of values
        """
        res = {}
        operation_obj = self.browse(cr, uid, ids, context=context)
        for operation in operation_obj:
            res[operation.id] = operation.production_id.date_planned
        return res

    def calc_delay(self, cr, uid, vals):
        """ Calculates delay of work order.
        @return: Delay
        """
        code_lst = []
        time_lst = []

        code_ids = self.pool.get('mrp_operations.operation.code').search(
            cr, uid, [('id', '=', vals['code_id'])])
        code = self.pool.get('mrp_operations.operation.code').browse(
            cr, uid, code_ids)[0]

        oper_ids = self.search(cr, uid,
                               [('production_id', '=', vals['production_id']),
                                ('workcenter_id', '=', vals['workcenter_id'])])
        oper_objs = self.browse(cr, uid, oper_ids)

        for oper in oper_objs:
            code_lst.append(oper.code_id.start_stop)
            time_lst.append(oper.date_start)

        code_lst.append(code.start_stop)
        time_lst.append(vals['date_start'])
        diff = 0
        for i in range(0, len(code_lst)):
            if code_lst[i] == 'pause' or code_lst[i] == 'done' or code_lst[
                    i] == 'cancel':
                if not i: continue
                if code_lst[i - 1] not in ('resume', 'start'):
                    continue
                a = datetime.strptime(time_lst[i - 1], '%Y-%m-%d %H:%M:%S')
                b = datetime.strptime(time_lst[i], '%Y-%m-%d %H:%M:%S')
                diff += (b - a).days * 24
                diff += (b - a).seconds / float(60 * 60)
        return diff

    def check_operation(self, cr, uid, vals):
        """ Finds which operation is called ie. start, pause, done, cancel.
        @param vals: Dictionary of values.
        @return: True or False
        """
        code_ids = self.pool.get('mrp_operations.operation.code').search(
            cr, uid, [('id', '=', vals['code_id'])])
        code = self.pool.get('mrp_operations.operation.code').browse(
            cr, uid, code_ids)[0]
        code_lst = []
        oper_ids = self.search(cr, uid,
                               [('production_id', '=', vals['production_id']),
                                ('workcenter_id', '=', vals['workcenter_id'])])
        oper_objs = self.browse(cr, uid, oper_ids)

        if not oper_objs:
            if code.start_stop != 'start':
                raise UserError(_('Operation is not started yet!'))
                return False
        else:
            for oper in oper_objs:
                code_lst.append(oper.code_id.start_stop)
            if code.start_stop == 'start':
                if 'start' in code_lst:
                    raise UserError(
                        _('Operation has already started! You can either Pause/Finish/Cancel the operation.'
                          ))
                    return False
            if code.start_stop == 'pause':
                if code_lst[len(code_lst) - 1] != 'resume' and code_lst[
                        len(code_lst) - 1] != 'start':
                    raise UserError(
                        _('In order to Pause the operation, it must be in the Start or Resume state!'
                          ))
                    return False
            if code.start_stop == 'resume':
                if code_lst[len(code_lst) - 1] != 'pause':
                    raise UserError(
                        _('In order to Resume the operation, it must be in the Pause state!'
                          ))
                    return False

            if code.start_stop == 'done':
                if code_lst[len(code_lst) - 1] != 'start' and code_lst[
                        len(code_lst) - 1] != 'resume':
                    raise UserError(
                        _('In order to Finish the operation, it must be in the Start or Resume state!'
                          ))
                    return False
                if 'cancel' in code_lst:
                    raise UserError(_('Operation is Already Cancelled!'))
                    return False
            if code.start_stop == 'cancel':
                if not 'start' in code_lst:
                    raise UserError(_('No operation to cancel.'))
                    return False
                if 'done' in code_lst:
                    raise UserError(_('Operation is already finished!'))
                    return False
        return True

    def write(self, cr, uid, ids, vals, context=None):
        oper_objs = self.browse(cr, uid, ids, context=context)[0]
        vals['production_id'] = oper_objs.production_id.id
        vals['workcenter_id'] = oper_objs.workcenter_id.id

        if 'code_id' in vals:
            self.check_operation(cr, uid, vals)

        if 'date_start' in vals:
            vals['date_start'] = vals['date_start']
            vals['code_id'] = oper_objs.code_id.id
            delay = self.calc_delay(cr, uid, vals)
            wc_op_id = self.pool.get('mrp.production.workcenter.line').search(
                cr, uid, [('workcenter_id', '=', vals['workcenter_id']),
                          ('production_id', '=', vals['production_id'])])
            self.pool.get('mrp.production.workcenter.line').write(
                cr, uid, wc_op_id, {'delay': delay})

        return super(mrp_operations_operation, self).write(cr,
                                                           uid,
                                                           ids,
                                                           vals,
                                                           context=context)

    def create(self, cr, uid, vals, context=None):
        workcenter_pool = self.pool.get('mrp.production.workcenter.line')
        code_ids = self.pool.get('mrp_operations.operation.code').search(
            cr, uid, [('id', '=', vals['code_id'])])
        code = self.pool.get('mrp_operations.operation.code').browse(
            cr, uid, code_ids, context=context)[0]
        wc_op_id = workcenter_pool.search(
            cr, uid, [('workcenter_id', '=', vals['workcenter_id']),
                      ('production_id', '=', vals['production_id'])])
        if code.start_stop in ('start', 'done', 'pause', 'cancel', 'resume'):
            if not wc_op_id:
                production_obj = self.pool.get('mrp.production').browse(
                    cr, uid, vals['production_id'], context=context)
                wc_op_id.append(
                    workcenter_pool.create(
                        cr, uid, {
                            'production_id': vals['production_id'],
                            'name': production_obj.product_id.name,
                            'workcenter_id': vals['workcenter_id']
                        }))
            if code.start_stop == 'start':
                workcenter_pool.action_start_working(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_start_working')

            if code.start_stop == 'done':
                workcenter_pool.action_done(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_done')
                self.pool.get('mrp.production').write(
                    cr, uid, vals['production_id'], {
                        'date_finished':
                        datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    })

            if code.start_stop == 'pause':
                workcenter_pool.action_pause(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_pause')

            if code.start_stop == 'resume':
                workcenter_pool.action_resume(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_resume')

            if code.start_stop == 'cancel':
                workcenter_pool.action_cancel(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_cancel')

        if not self.check_operation(cr, uid, vals):
            return
        delay = self.calc_delay(cr, uid, vals)
        line_vals = {}
        line_vals['delay'] = delay
        if vals.get('date_start', False):
            if code.start_stop == 'done':
                line_vals['date_finished'] = vals['date_start']
            elif code.start_stop == 'start':
                line_vals['date_start'] = vals['date_start']

        self.pool.get('mrp.production.workcenter.line').write(cr,
                                                              uid,
                                                              wc_op_id,
                                                              line_vals,
                                                              context=context)

        return super(mrp_operations_operation, self).create(cr,
                                                            uid,
                                                            vals,
                                                            context=context)

    def initialize_workflow_instance(self, cr, uid, context=None):
        mrp_production_workcenter_line = self.pool.get(
            'mrp.production.workcenter.line')
        line_ids = mrp_production_workcenter_line.search(cr,
                                                         uid, [],
                                                         context=context)
        mrp_production_workcenter_line.create_workflow(cr, uid, line_ids)
        return True

    _columns = {
        'production_id':
        fields.many2one('mrp.production', 'Production', required=True),
        'workcenter_id':
        fields.many2one('mrp.workcenter', 'Work Center', required=True),
        'code_id':
        fields.many2one('mrp_operations.operation.code', 'Code',
                        required=True),
        'date_start':
        fields.datetime('Start Date'),
        'date_finished':
        fields.datetime('End Date'),
        'order_date':
        fields.function(_get_order_date,
                        string='Order Date',
                        type='date',
                        store={
                            'mrp.production': (_order_date_search_production,
                                               ['date_planned'], 10)
                        }),
    }
    _defaults = {
        'date_start': lambda *a: datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    }
예제 #25
0
class mrp_production_workcenter_line(osv.osv):
    def _get_date_end(self, cr, uid, ids, field_name, arg, context=None):
        """ Finds ending date.
        @return: Dictionary of values.
        """
        ops = self.browse(cr, uid, ids, context=context)
        date_and_hours_by_cal = [(op.date_planned, op.hour,
                                  op.workcenter_id.calendar_id.id)
                                 for op in ops if op.date_planned]

        intervals = self.pool.get('resource.calendar').interval_get_multi(
            cr, uid, date_and_hours_by_cal)

        res = {}
        for op in ops:
            res[op.id] = False
            if op.date_planned:
                i = intervals.get((op.date_planned, op.hour,
                                   op.workcenter_id.calendar_id.id))
                if i:
                    res[op.id] = i[-1][1].strftime('%Y-%m-%d %H:%M:%S')
                else:
                    res[op.id] = op.date_planned
        return res

    def onchange_production_id(self,
                               cr,
                               uid,
                               ids,
                               production_id,
                               context=None):
        if not production_id:
            return {}
        production = self.pool.get('mrp.production').browse(cr,
                                                            uid,
                                                            production_id,
                                                            context=None)
        result = {
            'product': production.product_id.id,
            'qty': production.product_qty,
            'uom': production.product_uom.id,
        }
        return {'value': result}

    _inherit = 'mrp.production.workcenter.line'
    _order = "sequence, date_planned"

    _columns = {
       'state': fields.selection([('draft','Draft'),('cancel','Cancelled'),('pause','Pending'),('startworking', 'In Progress'),('done','Finished')],'Status', readonly=True, copy=False,
                                 help="* When a work order is created it is set in 'Draft' status.\n" \
                                       "* When user sets work order in start mode that time it will be set in 'In Progress' status.\n" \
                                       "* When work order is in running mode, during that time if user wants to stop or to make changes in order then can set in 'Pending' status.\n" \
                                       "* When the user cancels the work order it will be set in 'Canceled' status.\n" \
                                       "* When order is completely processed that time it is set in 'Finished' status."),
       'date_planned': fields.datetime('Scheduled Date', select=True),
       'date_planned_end': fields.function(_get_date_end, string='End Date', type='datetime'),
       'date_start': fields.datetime('Start Date'),
       'date_finished': fields.datetime('End Date'),
       'delay': fields.float('Working Hours',help="The elapsed time between operation start and stop in this Work Center",readonly=True),
       'production_state':fields.related('production_id','state',
            type='selection',
            selection=[('draft','Draft'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')],
            string='Production Status', readonly=True),
       'product':fields.related('production_id','product_id',type='many2one',relation='product.product',string='Product',
            readonly=True),
       'qty':fields.related('production_id','product_qty',type='float',string='Qty',readonly=True, store=True),
       'uom':fields.related('production_id','product_uom',type='many2one',relation='product.uom',string='Unit of Measure',readonly=True),
    }

    _defaults = {'state': 'draft', 'delay': 0.0, 'production_state': 'draft'}

    def modify_production_order_state(self, cr, uid, ids, action):
        """ Modifies production order state if work order state is changed.
        @param action: Action to perform.
        @return: Nothing
        """
        prod_obj_pool = self.pool.get('mrp.production')
        oper_obj = self.browse(cr, uid, ids)[0]
        prod_obj = oper_obj.production_id
        if action == 'start':
            if prod_obj.state == 'confirmed':
                prod_obj_pool.force_production(cr, uid, [prod_obj.id])
                prod_obj_pool.signal_workflow(cr, uid, [prod_obj.id],
                                              'button_produce')
            elif prod_obj.state == 'ready':
                prod_obj_pool.signal_workflow(cr, uid, [prod_obj.id],
                                              'button_produce')
            elif prod_obj.state == 'in_production':
                return
            else:
                raise UserError(
                    _('Manufacturing order cannot be started in state "%s"!') %
                    (prod_obj.state, ))
        else:
            open_count = self.search_count(
                cr, uid, [('production_id', '=', prod_obj.id),
                          ('state', '!=', 'done')])
            flag = not bool(open_count)
            if flag:
                button_produce_done = True
                for production in prod_obj_pool.browse(cr,
                                                       uid, [prod_obj.id],
                                                       context=None):
                    if production.move_lines or production.move_created_ids:
                        moves = production.move_lines + production.move_created_ids
                        # If tracking is activated, we want to make sure the user will enter the
                        # serial numbers.
                        if moves.filtered(
                                lambda r: r.product_id.tracking != 'none'):
                            button_produce_done = False
                        else:
                            prod_obj_pool.action_produce(
                                cr,
                                uid,
                                production.id,
                                production.product_qty,
                                'consume_produce',
                                context=None)
                if button_produce_done:
                    prod_obj_pool.signal_workflow(cr, uid,
                                                  [oper_obj.production_id.id],
                                                  'button_produce_done')
        return

    def write(self, cr, uid, ids, vals, context=None, update=True):
        result = super(mrp_production_workcenter_line,
                       self).write(cr, uid, ids, vals, context=context)
        prod_obj = self.pool.get('mrp.production')
        if vals.get('date_planned', False) and update:
            for prod in self.browse(cr, uid, ids, context=context):
                if prod.production_id.workcenter_lines:
                    dstart = min(
                        vals['date_planned'],
                        prod.production_id.workcenter_lines[0]['date_planned'])
                    prod_obj.write(cr,
                                   uid, [prod.production_id.id],
                                   {'date_start': dstart},
                                   context=context,
                                   mini=False)
        return result

    def action_draft(self, cr, uid, ids, context=None):
        """ Sets state to draft.
        @return: True
        """
        return self.write(cr, uid, ids, {'state': 'draft'}, context=context)

    def action_start_working(self, cr, uid, ids, context=None):
        """ Sets state to start working and writes starting date.
        @return: True
        """
        self.modify_production_order_state(cr, uid, ids, 'start')
        self.write(cr,
                   uid,
                   ids, {
                       'state': 'startworking',
                       'date_start': time.strftime('%Y-%m-%d %H:%M:%S')
                   },
                   context=context)
        return True

    def action_done(self, cr, uid, ids, context=None):
        """ Sets state to done, writes finish date and calculates delay.
        @return: True
        """
        delay = 0.0
        date_now = time.strftime('%Y-%m-%d %H:%M:%S')
        obj_line = self.browse(cr, uid, ids[0])

        date_start = datetime.strptime(obj_line.date_start,
                                       '%Y-%m-%d %H:%M:%S')
        date_finished = datetime.strptime(date_now, '%Y-%m-%d %H:%M:%S')
        delay += (date_finished - date_start).days * 24
        delay += (date_finished - date_start).seconds / float(60 * 60)

        self.write(cr,
                   uid,
                   ids, {
                       'state': 'done',
                       'date_finished': date_now,
                       'delay': delay
                   },
                   context=context)
        self.modify_production_order_state(cr, uid, ids, 'done')
        return True

    def action_cancel(self, cr, uid, ids, context=None):
        """ Sets state to cancel.
        @return: True
        """
        return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)

    def action_pause(self, cr, uid, ids, context=None):
        """ Sets state to pause.
        @return: True
        """
        return self.write(cr, uid, ids, {'state': 'pause'}, context=context)

    def action_resume(self, cr, uid, ids, context=None):
        """ Sets state to startworking.
        @return: True
        """
        return self.write(cr,
                          uid,
                          ids, {'state': 'startworking'},
                          context=context)
예제 #26
0
def function_fn_write(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context):
    """ just so CreatorCase.export can be used
    """
    pass

models = [
    ('boolean', fields.boolean()),
    ('integer', fields.integer()),
    ('float', fields.float()),
    ('decimal', fields.float(digits=(16, 3))),
    ('string.bounded', fields.char('unknown', size=16)),
    ('string.required', fields.char('unknown', size=None, required=True)),
    ('string', fields.char('unknown', size=None)),
    ('date', fields.date()),
    ('datetime', fields.datetime()),
    ('text', fields.text()),
    ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux"), (4, '')])),
    # here use size=-1 to store the values as integers instead of strings
    ('selection.function', fields.selection(selection_fn, size=-1)),
    # just relate to an integer
    ('many2one', fields.many2one('export.integer')),
    ('one2many', fields.one2many('export.one2many.child', 'parent_id')),
    ('many2many', fields.many2many('export.many2many.other')),
    ('function', fields.function(function_fn, fnct_inv=function_fn_write, type="integer")),
    # related: specialization of fields.function, should work the same way
    # TODO: reference
]

for name, field in models:
    class NewModel(orm.Model):
예제 #27
0
class purchase_report(osv.osv):
    _name = "purchase.report"
    _description = "Purchases Orders"
    _auto = False
    _columns = {
        'date': fields.datetime('Order Date', readonly=True, help="Date on which this document has been created"),  # TDE FIXME master: rename into date_order
        'state': fields.selection([('draft', 'Draft RFQ'),
                                   ('sent', 'RFQ Sent'),
                                   ('to approve', 'To Approve'),
                                   ('purchase', 'Purchase Order'),
                                   ('done', 'Done'),
                                   ('cancel', 'Cancelled')
                                  ],'Order Status', readonly=True),
        'product_id':fields.many2one('product.product', 'Product', readonly=True),
        'picking_type_id': fields.many2one('stock.warehouse', 'Warehouse', readonly=True),
        'partner_id':fields.many2one('res.partner', 'Vendor', readonly=True),
        'date_approve':fields.date('Date Approved', readonly=True),
        'product_uom' : fields.many2one('product.uom', 'Reference Unit of Measure', required=True),
        'company_id':fields.many2one('res.company', 'Company', readonly=True),
        'currency_id': fields.many2one('res.currency', 'Currency', readonly=True),
        'user_id':fields.many2one('res.users', 'Responsible', readonly=True),
        'delay':fields.float('Days to Validate', digits=(16,2), readonly=True),
        'delay_pass':fields.float('Days to Deliver', digits=(16,2), readonly=True),
        'quantity': fields.float('Product Quantity', readonly=True),  # TDE FIXME master: rename into unit_quantity
        'price_total': fields.float('Total Price', readonly=True),
        'price_average': fields.float('Average Price', readonly=True, group_operator="avg"),
        'negociation': fields.float('Purchase-Standard Price', readonly=True, group_operator="avg"),
        'price_standard': fields.float('Products Value', readonly=True, group_operator="sum"),
        'nbr': fields.integer('# of Lines', readonly=True),  # TDE FIXME master: rename into nbr_lines
        'category_id': fields.many2one('product.category', 'Product Category', readonly=True),
        'product_tmpl_id': fields.many2one('product.template', 'Product Template', readonly=True),
        'country_id': fields.many2one('res.country', 'Partner Country', readonly=True),
        'fiscal_position_id': fields.many2one('account.fiscal.position', string='Fiscal Position', oldname='fiscal_position', readonly=True),
        'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
        'commercial_partner_id': fields.many2one('res.partner', 'Commercial Entity', readonly=True),
    }
    _order = 'date desc, price_total desc'
    def init(self, cr):
        tools.sql.drop_view_if_exists(cr, 'purchase_report')
        cr.execute("""
            create or replace view purchase_report as (
                WITH currency_rate (currency_id, rate, date_start, date_end) AS (
                    SELECT r.currency_id, r.rate, r.name AS date_start,
                        (SELECT name FROM res_currency_rate r2
                        WHERE r2.name > r.name AND
                            r2.currency_id = r.currency_id
                         ORDER BY r2.name ASC
                         LIMIT 1) AS date_end
                    FROM res_currency_rate r
                )
                select
                    min(l.id) as id,
                    s.date_order as date,
                    s.state,
                    s.date_approve,
                    s.dest_address_id,
                    spt.warehouse_id as picking_type_id,
                    s.partner_id as partner_id,
                    s.create_uid as user_id,
                    s.company_id as company_id,
                    s.fiscal_position_id as fiscal_position_id,
                    l.product_id,
                    p.product_tmpl_id,
                    t.categ_id as category_id,
                    t.uom_id as product_uom,
                    sum(l.product_qty/u.factor*u2.factor) as quantity,
                    extract(epoch from age(s.date_approve,s.date_order))/(24*60*60)::decimal(16,2) as delay,
                    extract(epoch from age(l.date_planned,s.date_order))/(24*60*60)::decimal(16,2) as delay_pass,
                    count(*) as nbr,
                    sum(l.price_unit * COALESCE(cr.rate, 1.0) * l.product_qty)::decimal(16,2) as price_total,
                    avg(100.0 * (l.price_unit * COALESCE(cr.rate,1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation,
                    sum(ip.value_float*l.product_qty/u.factor*u2.factor)::decimal(16,2) as price_standard,
                    (sum(l.product_qty * COALESCE(cr.rate, 1.0) * l.price_unit)/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
                    partner.country_id as country_id,
                    partner.commercial_partner_id as commercial_partner_id,
                    analytic_account.id as account_analytic_id
                from purchase_order_line l
                    join purchase_order s on (l.order_id=s.id)
                    join res_partner partner on s.partner_id = partner.id
                        left join product_product p on (l.product_id=p.id)
                            left join product_template t on (p.product_tmpl_id=t.id)
                            LEFT JOIN ir_property ip ON (ip.name='standard_price' AND ip.res_id=CONCAT('product.template,',t.id) AND ip.company_id=s.company_id)
                    left join product_uom u on (u.id=l.product_uom)
                    left join product_uom u2 on (u2.id=t.uom_id)
                    left join stock_picking_type spt on (spt.id=s.picking_type_id)
                    left join account_analytic_account analytic_account on (l.account_analytic_id = analytic_account.id)
                    left join currency_rate cr on (cr.currency_id = s.currency_id and
                        cr.date_start <= coalesce(s.date_order, now()) and
                        (cr.date_end is null or cr.date_end > coalesce(s.date_order, now())))
                group by
                    s.company_id,
                    s.create_uid,
                    s.partner_id,
                    u.factor,
                    l.price_unit,
                    s.date_approve,
                    l.date_planned,
                    l.product_uom,
                    s.dest_address_id,
                    s.fiscal_position_id,
                    l.product_id,
                    p.product_tmpl_id,
                    t.categ_id,
                    s.date_order,
                    s.state,
                    spt.warehouse_id,
                    u.uom_type,
                    u.category_id,
                    t.uom_id,
                    u.id,
                    u2.factor,
                    partner.country_id,
                    partner.commercial_partner_id,
                    analytic_account.id
            )
        """)
예제 #28
0
파일: procurement.py 프로젝트: LiberTang0/5
class procurement_order(osv.osv):
    """
    Procurement Orders
    """
    _name = "procurement.order"
    _description = "Procurement"
    _order = 'priority desc, date_planned, id asc'
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _log_create = False
    _columns = {
        'name':
        fields.text('Description', required=True),
        'origin':
        fields.char(
            'Source Document',
            help="Reference of the document that created this Procurement.\n"
            "This is automatically completed by eCore."),
        'company_id':
        fields.many2one('res.company', 'Company', required=True),

        # These two fields are used for shceduling
        'priority':
        fields.selection(PROCUREMENT_PRIORITIES,
                         'Priority',
                         required=True,
                         select=True,
                         track_visibility='onchange'),
        'date_planned':
        fields.datetime('Scheduled Date',
                        required=True,
                        select=True,
                        track_visibility='onchange'),
        'group_id':
        fields.many2one('procurement.group', 'Procurement Group'),
        'rule_id':
        fields.many2one(
            'procurement.rule',
            'Rule',
            track_visibility='onchange',
            help=
            "Chosen rule for the procurement resolution. Usually chosen by the system but can be manually set by the procurement manager to force an unusual behavior."
        ),
        'product_id':
        fields.many2one('product.product',
                        'Product',
                        required=True,
                        states={'confirmed': [('readonly', False)]},
                        readonly=True),
        'product_qty':
        fields.float(
            'Quantity',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            required=True,
            states={'confirmed': [('readonly', False)]},
            readonly=True),
        'product_uom':
        fields.many2one('product.uom',
                        'Product Unit of Measure',
                        required=True,
                        states={'confirmed': [('readonly', False)]},
                        readonly=True),
        'state':
        fields.selection([('cancel', 'Cancelled'), ('confirmed', 'Confirmed'),
                          ('exception', 'Exception'), ('running', 'Running'),
                          ('done', 'Done')],
                         'Status',
                         required=True,
                         track_visibility='onchange',
                         copy=False),
    }

    _defaults = {
        'state':
        'confirmed',
        'priority':
        '1',
        'date_planned':
        lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
        'company_id':
        lambda self, cr, uid, c: self.pool.get('res.company').
        _company_default_get(cr, uid, 'procurement.order', context=c)
    }

    def _needaction_domain_get(self, cr, uid, context=None):
        return [('state', '=', 'exception')]

    def unlink(self, cr, uid, ids, context=None):
        procurements = self.read(cr, uid, ids, ['state'], context=context)
        unlink_ids = []
        for s in procurements:
            if s['state'] == 'cancel':
                unlink_ids.append(s['id'])
            else:
                raise UserError(
                    _('Cannot delete Procurement Order(s) which are in %s state.'
                      ) % s['state'])
        return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)

    def create(self, cr, uid, vals, context=None):
        context = context or {}
        procurement_id = super(procurement_order, self).create(cr,
                                                               uid,
                                                               vals,
                                                               context=context)
        if not context.get('procurement_autorun_defer'):
            self.run(cr, uid, [procurement_id], context=context)
        return procurement_id

    def do_view_procurements(self, cr, uid, ids, context=None):
        '''
        This function returns an action that display existing procurement orders
        of same procurement group of given ids.
        '''
        act_obj = self.pool.get('ir.actions.act_window')
        action_id = self.pool.get('ir.model.data').xmlid_to_res_id(
            cr,
            uid,
            'procurement.do_view_procurements',
            raise_if_not_found=True)
        result = act_obj.read(cr, uid, [action_id], context=context)[0]
        group_ids = set([
            proc.group_id.id
            for proc in self.browse(cr, uid, ids, context=context)
            if proc.group_id
        ])
        result['domain'] = "[('group_id','in',[" + ','.join(
            map(str, list(group_ids))) + "])]"
        return result

    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
        """ Finds UoM of changed product.
        @param product_id: Changed id of product.
        @return: Dictionary of values.
        """
        if product_id:
            w = self.pool.get('product.product').browse(cr,
                                                        uid,
                                                        product_id,
                                                        context=context)
            v = {
                'product_uom': w.uom_id.id,
            }
            return {'value': v}
        return {}

    def get_cancel_ids(self, cr, uid, ids, context=None):
        return [
            proc.id for proc in self.browse(cr, uid, ids, context=context)
            if proc.state != 'done'
        ]

    def cancel(self, cr, uid, ids, context=None):
        #cancel only the procurements that aren't done already
        to_cancel_ids = self.get_cancel_ids(cr, uid, ids, context=context)
        if to_cancel_ids:
            return self.write(cr,
                              uid,
                              to_cancel_ids, {'state': 'cancel'},
                              context=context)

    def reset_to_confirmed(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': 'confirmed'},
                          context=context)

    @api.v8
    def run(self, autocommit=False):
        return self._model.run(self._cr,
                               self._uid,
                               self.ids,
                               autocommit=False,
                               context=self._context)

    @api.v7
    def run(self, cr, uid, ids, autocommit=False, context=None):
        for procurement_id in ids:
            #we intentionnaly do the browse under the for loop to avoid caching all ids which would be resource greedy
            #and useless as we'll make a refresh later that will invalidate all the cache (and thus the next iteration
            #will fetch all the ids again)
            procurement = self.browse(cr, uid, procurement_id, context=context)
            if procurement.state not in ("running", "done"):
                try:
                    if self._assign(cr, uid, procurement, context=context):
                        res = self._run(cr,
                                        uid,
                                        procurement,
                                        context=context or {})
                        if res:
                            self.write(cr,
                                       uid, [procurement.id],
                                       {'state': 'running'},
                                       context=context)
                        else:
                            self.write(cr,
                                       uid, [procurement.id],
                                       {'state': 'exception'},
                                       context=context)
                    else:
                        self.message_post(
                            cr,
                            uid, [procurement.id],
                            body=_('No rule matching this procurement'),
                            context=context)
                        self.write(cr,
                                   uid, [procurement.id],
                                   {'state': 'exception'},
                                   context=context)
                    if autocommit:
                        cr.commit()
                except OperationalError:
                    if autocommit:
                        cr.rollback()
                        continue
                    else:
                        raise
        return True

    def check(self, cr, uid, ids, autocommit=False, context=None):
        done_ids = []
        for procurement in self.browse(cr, uid, ids, context=context):
            try:
                result = self._check(cr, uid, procurement, context=context)
                if result:
                    done_ids.append(procurement.id)
                if autocommit:
                    cr.commit()
            except OperationalError:
                if autocommit:
                    cr.rollback()
                    continue
                else:
                    raise
        if done_ids:
            self.write(cr, uid, done_ids, {'state': 'done'}, context=context)
        return done_ids

    #
    # Method to overwrite in different procurement modules
    #
    def _find_suitable_rule(self, cr, uid, procurement, context=None):
        '''This method returns a procurement.rule that depicts what to do with the given procurement
        in order to complete its needs. It returns False if no suiting rule is found.
            :param procurement: browse record
            :rtype: int or False
        '''
        return False

    def _assign(self, cr, uid, procurement, context=None):
        '''This method check what to do with the given procurement in order to complete its needs.
        It returns False if no solution is found, otherwise it stores the matching rule (if any) and
        returns True.
            :param procurement: browse record
            :rtype: boolean
        '''
        #if the procurement already has a rule assigned, we keep it (it has a higher priority as it may have been chosen manually)
        if procurement.rule_id:
            return True
        elif procurement.product_id.type not in ('service', 'digital'):
            rule_id = self._find_suitable_rule(cr,
                                               uid,
                                               procurement,
                                               context=context)
            if rule_id:
                self.write(cr,
                           uid, [procurement.id], {'rule_id': rule_id},
                           context=context)
                return True
        return False

    def _run(self, cr, uid, procurement, context=None):
        '''This method implements the resolution of the given procurement
            :param procurement: browse record
            :returns: True if the resolution of the procurement was a success, False otherwise to set it in exception
        '''
        return True

    def _check(self, cr, uid, procurement, context=None):
        '''Returns True if the given procurement is fulfilled, False otherwise
            :param procurement: browse record
            :rtype: boolean
        '''
        return False

    #
    # Scheduler
    #
    def run_scheduler(self,
                      cr,
                      uid,
                      use_new_cursor=False,
                      company_id=False,
                      context=None):
        '''
        Call the scheduler to check the procurement order. This is intented to be done for all existing companies at
        the same time, so we're running all the methods as SUPERUSER to avoid intercompany and access rights issues.

        @param self: The object pointer
        @param cr: The current row, from the database cursor,
        @param uid: The current user ID for security checks
        @param ids: List of selected IDs
        @param use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
            This is appropriate for batch jobs only.
        @param context: A standard dictionary for contextual values
        @return:  Dictionary of values
        '''
        if context is None:
            context = {}
        try:
            if use_new_cursor:
                cr = ecore.registry(cr.dbname).cursor()

            # Run confirmed procurements
            dom = [('state', '=', 'confirmed')]
            if company_id:
                dom += [('company_id', '=', company_id)]
            prev_ids = []
            while True:
                ids = self.search(cr, SUPERUSER_ID, dom, context=context)
                if not ids or prev_ids == ids:
                    break
                else:
                    prev_ids = ids
                self.run(cr,
                         SUPERUSER_ID,
                         ids,
                         autocommit=use_new_cursor,
                         context=context)
                if use_new_cursor:
                    cr.commit()

            # Check if running procurements are done
            offset = 0
            dom = [('state', '=', 'running')]
            if company_id:
                dom += [('company_id', '=', company_id)]
            prev_ids = []
            while True:
                ids = self.search(cr,
                                  SUPERUSER_ID,
                                  dom,
                                  offset=offset,
                                  context=context)
                if not ids or prev_ids == ids:
                    break
                else:
                    prev_ids = ids
                self.check(cr,
                           SUPERUSER_ID,
                           ids,
                           autocommit=use_new_cursor,
                           context=context)
                if use_new_cursor:
                    cr.commit()

        finally:
            if use_new_cursor:
                try:
                    cr.close()
                except Exception:
                    pass

        return {}
예제 #29
0
파일: project_issue.py 프로젝트: ecoreos/hz
class project_issue(osv.Model):
    _name = "project.issue"
    _description = "Project Issue"
    _order = "priority desc, create_date desc"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _mail_post_access = 'read'

    def _get_default_partner(self, cr, uid, context=None):
        if context is None:
            context = {}
        if 'default_project_id' in context:
            project = self.pool.get('project.project').browse(
                cr, uid, context['default_project_id'], context=context)
            if project and project.partner_id:
                return project.partner_id.id
        return False

    def _get_default_stage_id(self, cr, uid, context=None):
        """ Gives default stage_id """
        if context is None:
            context = {}
        return self.stage_find(cr,
                               uid, [],
                               context.get('default_project_id'),
                               [('fold', '=', False)],
                               context=context)

    def _read_group_stage_ids(self,
                              cr,
                              uid,
                              ids,
                              domain,
                              read_group_order=None,
                              access_rights_uid=None,
                              context=None):
        if context is None:
            context = {}
        access_rights_uid = access_rights_uid or uid
        stage_obj = self.pool.get('project.task.type')
        order = stage_obj._order
        # lame hack to allow reverting search, should just work in the trivial case
        if read_group_order == 'stage_id desc':
            order = "%s desc" % order
        # retrieve team_id from the context, add them to already fetched columns (ids)
        if 'default_project_id' in context:
            search_domain = [
                '|', ('project_ids', '=', context['default_project_id']),
                ('id', 'in', ids)
            ]
        else:
            search_domain = [('id', 'in', ids)]
        # perform search
        stage_ids = stage_obj._search(cr,
                                      uid,
                                      search_domain,
                                      order=order,
                                      access_rights_uid=access_rights_uid,
                                      context=context)
        result = stage_obj.name_get(cr,
                                    access_rights_uid,
                                    stage_ids,
                                    context=context)
        # restore order of the search
        result.sort(
            lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))

        fold = {}
        for stage in stage_obj.browse(cr,
                                      access_rights_uid,
                                      stage_ids,
                                      context=context):
            fold[stage.id] = stage.fold or False
        return result, fold

    def _compute_day(self, cr, uid, ids, fields, args, context=None):
        """
        @param cr: the current row, from the database cursor,
        @param uid: the current user’s ID for security checks,
        @param ids: List of Openday’s IDs
        @return: difference between current date and log date
        @param context: A standard dictionary for contextual values
        """
        Calendar = self.pool['resource.calendar']

        res = dict((res_id, {}) for res_id in ids)
        for issue in self.browse(cr, uid, ids, context=context):
            values = {
                'day_open': 0.0,
                'day_close': 0.0,
                'working_hours_open': 0.0,
                'working_hours_close': 0.0,
                'days_since_creation': 0.0,
                'inactivity_days': 0.0,
            }
            # if the working hours on the project are not defined, use default ones (8 -> 12 and 13 -> 17 * 5), represented by None
            calendar_id = None
            if issue.project_id and issue.project_id.resource_calendar_id:
                calendar_id = issue.project_id.resource_calendar_id.id

            dt_create_date = datetime.strptime(issue.create_date,
                                               DEFAULT_SERVER_DATETIME_FORMAT)

            if issue.date_open:
                dt_date_open = datetime.strptime(
                    issue.date_open, DEFAULT_SERVER_DATETIME_FORMAT)
                values['day_open'] = (dt_date_open - dt_create_date
                                      ).total_seconds() / (24.0 * 3600)
                values['working_hours_open'] = Calendar._interval_hours_get(
                    cr,
                    uid,
                    calendar_id,
                    dt_create_date,
                    dt_date_open,
                    timezone_from_uid=issue.user_id.id or uid,
                    exclude_leaves=False,
                    context=context)

            if issue.date_closed:
                dt_date_closed = datetime.strptime(
                    issue.date_closed, DEFAULT_SERVER_DATETIME_FORMAT)
                values['day_close'] = (dt_date_closed - dt_create_date
                                       ).total_seconds() / (24.0 * 3600)
                values['working_hours_close'] = Calendar._interval_hours_get(
                    cr,
                    uid,
                    calendar_id,
                    dt_create_date,
                    dt_date_closed,
                    timezone_from_uid=issue.user_id.id or uid,
                    exclude_leaves=False,
                    context=context)

            days_since_creation = datetime.today() - dt_create_date
            values['days_since_creation'] = days_since_creation.days
            if issue.date_action_last:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.date_action_last, DEFAULT_SERVER_DATETIME_FORMAT)
            elif issue.date_last_stage_update:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.date_last_stage_update,
                    DEFAULT_SERVER_DATETIME_FORMAT)
            else:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.create_date, DEFAULT_SERVER_DATETIME_FORMAT)
            values['inactivity_days'] = inactive_days.days

            # filter only required values
            for field in fields:
                res[issue.id][field] = values[field]

        return res

    def on_change_project(self, cr, uid, ids, project_id, context=None):
        if project_id:
            project = self.pool.get('project.project').browse(cr,
                                                              uid,
                                                              project_id,
                                                              context=context)
            if project and project.partner_id:
                return {'value': {'partner_id': project.partner_id.id}}
        return {'value': {'partner_id': False}}

    _columns = {
        'id': fields.integer('ID', readonly=True),
        'name': fields.char('Issue', required=True),
        'active': fields.boolean('Active', required=False),
        'create_date': fields.datetime('Creation Date', readonly=True, select=True),
        'write_date': fields.datetime('Update Date', readonly=True),
        'days_since_creation': fields.function(_compute_day, string='Days since creation date', \
                                               multi='compute_day', type="integer", help="Difference in days between creation date and current date"),
        'date_deadline': fields.date('Deadline'),
        'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id',\
                        select=True, help='Sales team to which Case belongs to.\
                             Define Responsible user and Email account for mail gateway.'                                                                                         ),
        'partner_id': fields.many2one('res.partner', 'Contact', select=1),
        'company_id': fields.many2one('res.company', 'Company'),
        'description': fields.text('Private Note'),
        'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
                                         track_visibility='onchange',
                                         help="A Issue's kanban state indicates special situations affecting it:\n"
                                              " * Normal is the default situation\n"
                                              " * Blocked indicates something is preventing the progress of this issue\n"
                                              " * Ready for next stage indicates the issue is ready to be pulled to the next stage",
                                         required=True),
        'email_from': fields.char('Email', size=128, help="These people will receive email.", select=1),
        'email_cc': fields.char('Watchers Emails', size=256, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
        'date_open': fields.datetime('Assigned', readonly=True, select=True),
        # Project Issue fields
        'date_closed': fields.datetime('Closed', readonly=True, select=True),
        'date': fields.datetime('Date'),
        'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
        'channel': fields.char('Channel', help="Communication channel."),
        'tag_ids': fields.many2many('project.tags', string='Tags'),
        'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority', select=True),
        'stage_id': fields.many2one ('project.task.type', 'Stage',
                        track_visibility='onchange', select=True,
                        domain="[('project_ids', '=', project_id)]", copy=False),
        'project_id': fields.many2one('project.project', 'Project', track_visibility='onchange', select=True),
        'duration': fields.float('Duration'),
        'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]",
            help="You can link this issue to an existing task or directly create a new one from here"),
        'day_open': fields.function(_compute_day, string='Days to Assign',
                                    multi='compute_day', type="float",
                                    store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
        'day_close': fields.function(_compute_day, string='Days to Close',
                                     multi='compute_day', type="float",
                                     store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
        'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1, track_visibility='onchange'),
        'working_hours_open': fields.function(_compute_day, string='Working Hours to assign the Issue',
                                              multi='compute_day', type="float",
                                              store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
        'working_hours_close': fields.function(_compute_day, string='Working Hours to close the Issue',
                                               multi='compute_day', type="float",
                                               store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
        'inactivity_days': fields.function(_compute_day, string='Days since last action',
                                           multi='compute_day', type="integer", help="Difference in days between last action and current date"),
        'color': fields.integer('Color Index'),
        'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
        'date_action_last': fields.datetime('Last Action', readonly=1),
        'date_action_next': fields.datetime('Next Action', readonly=1),
        'legend_blocked': fields.related("stage_id", "legend_blocked", type="char", string='Kanban Blocked Explanation'),
        'legend_done': fields.related("stage_id", "legend_done", type="char", string='Kanban Valid Explanation'),
        'legend_normal': fields.related("stage_id", "legend_normal", type="char", string='Kanban Ongoing Explanation'),
    }

    _defaults = {
        'active':
        1,
        'team_id':
        lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(
            cr, uid, context=c),
        'stage_id':
        lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
        'company_id':
        lambda s, cr, uid, c: s.pool['res.users']._get_company(
            cr, uid, context=c),
        'priority':
        '0',
        'kanban_state':
        'normal',
        'date_last_stage_update':
        fields.datetime.now,
        'user_id':
        lambda obj, cr, uid, context: uid,
    }

    _group_by_full = {'stage_id': _read_group_stage_ids}

    def copy(self, cr, uid, id, default=None, context=None):
        issue = self.read(cr, uid, [id], ['name'], context=context)[0]
        if not default:
            default = {}
        default = default.copy()
        default.update(name=_('%s (copy)') % (issue['name']))
        return super(project_issue, self).copy(cr,
                                               uid,
                                               id,
                                               default=default,
                                               context=context)

    def create(self, cr, uid, vals, context=None):
        context = dict(context or {})
        if vals.get('project_id') and not context.get('default_project_id'):
            context['default_project_id'] = vals.get('project_id')
        if vals.get('user_id') and not vals.get('date_open'):
            vals['date_open'] = fields.datetime.now()
        if 'stage_id' in vals:
            vals.update(
                self.onchange_stage_id(cr,
                                       uid,
                                       None,
                                       vals.get('stage_id'),
                                       context=context)['value'])

        # context: no_log, because subtype already handle this
        create_context = dict(context, mail_create_nolog=True)
        return super(project_issue, self).create(cr,
                                                 uid,
                                                 vals,
                                                 context=create_context)

    def write(self, cr, uid, ids, vals, context=None):
        # stage change: update date_last_stage_update
        if 'stage_id' in vals:
            vals.update(
                self.onchange_stage_id(cr,
                                       uid,
                                       ids,
                                       vals.get('stage_id'),
                                       context=context)['value'])
            vals['date_last_stage_update'] = fields.datetime.now()
            if 'kanban_state' not in vals:
                vals['kanban_state'] = 'normal'
        # user_id change: update date_open
        if vals.get('user_id') and 'date_open' not in vals:
            vals['date_open'] = fields.datetime.now()

        return super(project_issue, self).write(cr, uid, ids, vals, context)

    def onchange_task_id(self, cr, uid, ids, task_id, context=None):
        if not task_id:
            return {'value': {}}
        task = self.pool.get('project.task').browse(cr,
                                                    uid,
                                                    task_id,
                                                    context=context)
        return {
            'value': {
                'user_id': task.user_id.id,
            }
        }

    def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
        """ This function returns value of partner email address based on partner
            :param part: Partner's id
        """
        if partner_id:
            partner = self.pool['res.partner'].browse(cr, uid, partner_id,
                                                      context)
            return {'value': {'email_from': partner.email}}
        return {'value': {'email_from': False}}

    def get_empty_list_help(self, cr, uid, help, context=None):
        context = dict(context or {})
        context['empty_list_help_model'] = 'project.project'
        context['empty_list_help_id'] = context.get('default_project_id')
        context['empty_list_help_document_name'] = _("issues")
        return super(project_issue, self).get_empty_list_help(cr,
                                                              uid,
                                                              help,
                                                              context=context)

    # -------------------------------------------------------
    # Stage management
    # -------------------------------------------------------

    def onchange_stage_id(self, cr, uid, ids, stage_id, context=None):
        if not stage_id:
            return {'value': {}}
        stage = self.pool['project.task.type'].browse(cr,
                                                      uid,
                                                      stage_id,
                                                      context=context)
        if stage.fold:
            return {'value': {'date_closed': fields.datetime.now()}}
        return {'value': {'date_closed': False}}

    def stage_find(self,
                   cr,
                   uid,
                   cases,
                   team_id,
                   domain=[],
                   order='sequence',
                   context=None):
        """ Override of the base.stage method
            Parameter of the stage search taken from the issue:
            - type: stage type must be the same or 'both'
            - team_id: if set, stages must belong to this team or
              be a default case
        """
        if isinstance(cases, (int, long)):
            cases = self.browse(cr, uid, cases, context=context)
        # collect all team_ids
        team_ids = []
        if team_id:
            team_ids.append(team_id)
        for task in cases:
            if task.project_id:
                team_ids.append(task.project_id.id)
        # OR all team_ids and OR with case_default
        search_domain = []
        if team_ids:
            search_domain += [('|')] * (len(team_ids) - 1)
            for team_id in team_ids:
                search_domain.append(('project_ids', '=', team_id))
        search_domain += list(domain)
        # perform search, return the first found
        stage_ids = self.pool.get('project.task.type').search(cr,
                                                              uid,
                                                              search_domain,
                                                              order=order,
                                                              context=context)
        if stage_ids:
            return stage_ids[0]
        return False

    # -------------------------------------------------------
    # Mail gateway
    # -------------------------------------------------------

    def _track_subtype(self, cr, uid, ids, init_values, context=None):
        record = self.browse(cr, uid, ids[0], context=context)
        if 'kanban_state' in init_values and record.kanban_state == 'blocked':
            return 'project_issue.mt_issue_blocked'
        elif 'kanban_state' in init_values and record.kanban_state == 'done':
            return 'project_issue.mt_issue_ready'
        elif 'user_id' in init_values and record.user_id:  # assigned -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1:  # start stage -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values:
            return 'project_issue.mt_issue_stage'
        return super(project_issue, self)._track_subtype(cr,
                                                         uid,
                                                         ids,
                                                         init_values,
                                                         context=context)

    def _notification_group_recipients(self,
                                       cr,
                                       uid,
                                       ids,
                                       message,
                                       recipients,
                                       done_ids,
                                       group_data,
                                       context=None):
        """ Override the mail.thread method to handle project users and officers
        recipients. Indeed those will have specific action in their notification
        emails: creating tasks, assigning it. """
        group_project_user = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'project.group_project_user')
        for recipient in recipients:
            if recipient.id in done_ids:
                continue
            if recipient.user_ids and group_project_user in recipient.user_ids[
                    0].groups_id.ids:
                group_data['group_project_user'] |= recipient
                done_ids.add(recipient.id)
        return super(project_issue,
                     self)._notification_group_recipients(cr,
                                                          uid,
                                                          ids,
                                                          message,
                                                          recipients,
                                                          done_ids,
                                                          group_data,
                                                          context=context)

    def _notification_get_recipient_groups(self,
                                           cr,
                                           uid,
                                           ids,
                                           message,
                                           recipients,
                                           context=None):
        res = super(project_issue,
                    self)._notification_get_recipient_groups(cr,
                                                             uid,
                                                             ids,
                                                             message,
                                                             recipients,
                                                             context=context)

        new_action_id = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'project_issue.project_issue_categ_act0')
        take_action = self._notification_link_helper(cr,
                                                     uid,
                                                     ids,
                                                     'assign',
                                                     context=context)
        new_action = self._notification_link_helper(cr,
                                                    uid,
                                                    ids,
                                                    'new',
                                                    context=context,
                                                    action_id=new_action_id)

        task_record = self.browse(cr, uid, ids[0], context=context)
        actions = []
        if not task_record.user_id:
            actions.append({'url': take_action, 'title': _('I take it')})
        else:
            actions.append({'url': new_action, 'title': _('New Issue')})

        res['group_project_user'] = {'actions': actions}
        return res

    @api.cr_uid_context
    def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
        """ Override to get the reply_to of the parent project. """
        issues = self.browse(cr, SUPERUSER_ID, ids, context=context)
        project_ids = set(
            [issue.project_id.id for issue in issues if issue.project_id])
        aliases = self.pool['project.project'].message_get_reply_to(
            cr, uid, list(project_ids), default=default, context=context)
        return dict(
            (issue.id,
             aliases.get(issue.project_id and issue.project_id.id or 0, False))
            for issue in issues)

    def message_get_suggested_recipients(self, cr, uid, ids, context=None):
        recipients = super(project_issue,
                           self).message_get_suggested_recipients(
                               cr, uid, ids, context=context)
        try:
            for issue in self.browse(cr, uid, ids, context=context):
                if issue.partner_id:
                    issue._message_add_suggested_recipient(
                        recipients,
                        partner=issue.partner_id,
                        reason=_('Customer'))
                elif issue.email_from:
                    issue._message_add_suggested_recipient(
                        recipients,
                        email=issue.email_from,
                        reason=_('Customer Email'))
        except AccessError:  # no read access rights -> just ignore suggested recipients because this imply modifying followers
            pass
        return recipients

    def email_split(self, cr, uid, ids, msg, context=None):
        email_list = tools.email_split((msg.get('to') or '') + ',' +
                                       (msg.get('cc') or ''))
        # check left-part is not already an alias
        issue_ids = self.browse(cr, uid, ids, context=context)
        aliases = [
            issue.project_id.alias_name for issue in issue_ids
            if issue.project_id
        ]
        return filter(lambda x: x.split('@')[0] not in aliases, email_list)

    def message_new(self, cr, uid, msg, custom_values=None, context=None):
        """ Overrides mail_thread message_new that is called by the mailgateway
            through message_process.
            This override updates the document according to the email.
        """
        if custom_values is None:
            custom_values = {}
        context = dict(context or {}, state_to='draft')
        defaults = {
            'name': msg.get('subject') or _("No Subject"),
            'email_from': msg.get('from'),
            'email_cc': msg.get('cc'),
            'partner_id': msg.get('author_id', False),
            'user_id': False,
        }
        defaults.update(custom_values)

        res_id = super(project_issue, self).message_new(cr,
                                                        uid,
                                                        msg,
                                                        custom_values=defaults,
                                                        context=context)
        email_list = self.email_split(cr, uid, [res_id], msg, context=context)
        partner_ids = self._find_partner_from_emails(cr,
                                                     uid, [res_id],
                                                     email_list,
                                                     force_create=True,
                                                     context=context)
        self.message_subscribe(cr, uid, [res_id], partner_ids, context=context)
        return res_id

    def message_update(self,
                       cr,
                       uid,
                       ids,
                       msg,
                       update_vals=None,
                       context=None):
        """ Override to update the issue according to the email. """

        email_list = self.email_split(cr, uid, ids, msg, context=context)
        partner_ids = self._find_partner_from_emails(cr,
                                                     uid,
                                                     ids,
                                                     email_list,
                                                     force_create=True,
                                                     context=context)
        self.message_subscribe(cr, uid, ids, partner_ids, context=context)
        return super(project_issue,
                     self).message_update(cr,
                                          uid,
                                          ids,
                                          msg,
                                          update_vals=update_vals,
                                          context=context)

    @api.cr_uid_ids_context
    @api.returns('mail.message', lambda value: value.id)
    def message_post(self,
                     cr,
                     uid,
                     thread_id,
                     subtype=None,
                     context=None,
                     **kwargs):
        """ Overrides mail_thread message_post so that we can set the date of last action field when
            a new message is posted on the issue.
        """
        if context is None:
            context = {}
        res = super(project_issue, self).message_post(cr,
                                                      uid,
                                                      thread_id,
                                                      subtype=subtype,
                                                      context=context,
                                                      **kwargs)
        if thread_id and subtype:
            self.write(cr,
                       SUPERUSER_ID,
                       thread_id, {'date_action_last': fields.datetime.now()},
                       context=context)
        return res
예제 #30
0
class MailMailStats(osv.Model):
    """ MailMailStats models the statistics collected about emails. Those statistics
    are stored in a separated model and table to avoid bloating the mail_mail table
    with statistics values. This also allows to delete emails send with mass mailing
    without loosing the statistics about them. """

    _name = 'mail.mail.statistics'
    _description = 'Email Statistics'
    _rec_name = 'message_id'
    _order = 'message_id'

    def _compute_state(self, cr, uid, ids, field_names, arg, context=None):
        res = dict((i, {
            'state': 'outgoing',
            'state_update': fields.datetime.now()
        }) for i in ids)

        for stat in self.browse(cr, uid, ids, context=context):
            if stat.exception:
                res[stat.id]['state'] = 'exception'
            if stat.sent:
                res[stat.id]['state'] = 'sent'
            if stat.opened:
                res[stat.id]['state'] = 'opened'
            if stat.replied:
                res[stat.id]['state'] = 'replied'
            if stat.bounced:
                res[stat.id]['state'] = 'bounced'

        return res

    def _compute_recipient(self, cr, uid, ids, field_names, arg, context=None):
        res = dict.fromkeys(ids, '')
        for stat in self.browse(cr, uid, ids, context=context):
            if not self.pool.get(stat.model):
                continue
            target = self.pool[stat.model].browse(cr,
                                                  uid,
                                                  stat.res_id,
                                                  context=context)
            email = ''
            for email_field in ('email', 'email_from'):
                if email_field in target and target[email_field]:
                    email = ' <%s>' % target[email_field]
                    break
            res[stat.id] = '%s%s' % (target.display_name, email)
        return res

    __store = {
        _name: ((lambda s, c, u, i, t: i),
                ['exception', 'sent', 'opened', 'replied', 'bounced'], 10)
    }

    _columns = {
        'mail_mail_id':
        fields.many2one('mail.mail', 'Mail', ondelete='set null', select=True),
        'mail_mail_id_int':
        fields.integer(
            'Mail ID (tech)',
            help=
            'ID of the related mail_mail. This field is an integer field because'
            'the related mail_mail can be deleted separately from its statistics.'
            'However the ID is needed for several action and controllers.'),
        'message_id':
        fields.char('Message-ID'),
        'model':
        fields.char('Document model'),
        'res_id':
        fields.integer('Document ID'),
        # campaign / wave data
        'mass_mailing_id':
        fields.many2one(
            'mail.mass_mailing',
            'Mass Mailing',
            ondelete='set null',
        ),
        'mass_mailing_campaign_id':
        fields.related(
            'mass_mailing_id',
            'mass_mailing_campaign_id',
            type='many2one',
            ondelete='set null',
            relation='mail.mass_mailing.campaign',
            string='Mass Mailing Campaign',
            store=True,
            readonly=True,
        ),
        # Bounce and tracking
        'scheduled':
        fields.datetime('Scheduled',
                        help='Date when the email has been created'),
        'sent':
        fields.datetime('Sent', help='Date when the email has been sent'),
        'exception':
        fields.datetime(
            'Exception',
            help='Date of technical error leading to the email not being sent'
        ),
        'opened':
        fields.datetime(
            'Opened',
            help='Date when the email has been opened the first time'),
        'replied':
        fields.datetime(
            'Replied',
            help='Date when this email has been replied for the first time.'),
        'bounced':
        fields.datetime('Bounced', help='Date when this email has bounced.'),
        'links_click_ids':
        fields.one2many('link.tracker.click', 'mail_stat_id', 'Links click'),
        'state':
        fields.function(_compute_state,
                        string='State',
                        type="selection",
                        multi="state",
                        selection=[('outgoing', 'Outgoing'),
                                   ('exception', 'Exception'),
                                   ('sent', 'Sent'), ('opened', 'Opened'),
                                   ('replied', 'Replied'),
                                   ('bounced', 'Bounced')],
                        store=__store),
        'state_update':
        fields.function(_compute_state,
                        string='State Update',
                        type='datetime',
                        multi='state',
                        help='Last state update of the mail',
                        store=__store),
        'recipient':
        fields.function(_compute_recipient, string='Recipient', type='char'),
    }

    _defaults = {
        'scheduled': fields.datetime.now,
    }

    def create(self, cr, uid, values, context=None):
        if 'mail_mail_id' in values:
            values['mail_mail_id_int'] = values['mail_mail_id']
        res = super(MailMailStats, self).create(cr,
                                                uid,
                                                values,
                                                context=context)
        return res

    def _get_ids(self,
                 cr,
                 uid,
                 ids=None,
                 mail_mail_ids=None,
                 mail_message_ids=None,
                 domain=None,
                 context=None):
        if not ids and mail_mail_ids:
            base_domain = [('mail_mail_id_int', 'in', mail_mail_ids)]
        elif not ids and mail_message_ids:
            base_domain = [('message_id', 'in', mail_message_ids)]
        else:
            base_domain = [('id', 'in', ids or [])]
        if domain:
            base_domain = ['&'] + domain + base_domain
        return self.search(cr, uid, base_domain, context=context)

    def set_opened(self,
                   cr,
                   uid,
                   ids=None,
                   mail_mail_ids=None,
                   mail_message_ids=None,
                   context=None):
        stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids,
                                 [('opened', '=', False)], context)
        self.write(cr,
                   uid,
                   stat_ids, {'opened': fields.datetime.now()},
                   context=context)
        return stat_ids

    def set_replied(self,
                    cr,
                    uid,
                    ids=None,
                    mail_mail_ids=None,
                    mail_message_ids=None,
                    context=None):
        stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids,
                                 [('replied', '=', False)], context)
        self.write(cr,
                   uid,
                   stat_ids, {'replied': fields.datetime.now()},
                   context=context)
        return stat_ids

    def set_bounced(self,
                    cr,
                    uid,
                    ids=None,
                    mail_mail_ids=None,
                    mail_message_ids=None,
                    context=None):
        stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids,
                                 [('bounced', '=', False)], context)
        self.write(cr,
                   uid,
                   stat_ids, {'bounced': fields.datetime.now()},
                   context=context)
        return stat_ids
예제 #31
0
class crm_opportunity_report(osv.Model):
    """ CRM Opportunity Analysis """
    _name = "crm.opportunity.report"
    _auto = False
    _description = "CRM Opportunity Analysis"
    _rec_name = 'date_deadline'
    _inherit = ["utm.mixin"]

    _columns = {
        'date_deadline':
        fields.date('Expected Closing', readonly=True),
        'create_date':
        fields.datetime('Creation Date', readonly=True),
        'opening_date':
        fields.datetime('Assignation Date', readonly=True),
        'date_closed':
        fields.datetime('Close Date', readonly=True),
        'date_last_stage_update':
        fields.datetime('Last Stage Update', readonly=True),
        'active':
        fields.boolean('Active', readonly=True),

        # durations
        'delay_open':
        fields.float('Delay to Assign',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg",
                     help="Number of Days to open the case"),
        'delay_close':
        fields.float('Delay to Close',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg",
                     help="Number of Days to close the case"),
        'delay_expected':
        fields.float('Overpassed Deadline',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg"),
        'user_id':
        fields.many2one('res.users', 'User', readonly=True),
        'team_id':
        fields.many2one('crm.team',
                        'Sales Team',
                        oldname='section_id',
                        readonly=True),
        'nbr_activities':
        fields.integer('# of Activities', readonly=True),
        'country_id':
        fields.many2one('res.country', 'Country', readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company', readonly=True),
        'probability':
        fields.float('Probability',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg"),
        'total_revenue':
        fields.float('Total Revenue', digits=(16, 2), readonly=True),
        'expected_revenue':
        fields.float('Expected Revenue', digits=(16, 2), readonly=True),
        'stage_id':
        fields.many2one('crm.stage',
                        'Stage',
                        readonly=True,
                        domain="[('team_ids', '=', team_id)]"),
        'stage_name':
        fields.char('Stage Name', readonly=True),
        'partner_id':
        fields.many2one('res.partner', 'Partner', readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company', readonly=True),
        'priority':
        fields.selection(crm_stage.AVAILABLE_PRIORITIES, 'Priority'),
        'type':
        fields.selection(
            [
                ('lead', 'Lead'),
                ('opportunity', 'Opportunity'),
            ],
            'Type',
            help="Type is used to separate Leads and Opportunities"),
        'lost_reason':
        fields.many2one('crm.lost.reason', 'Lost Reason', readonly=True),
        'date_conversion':
        fields.datetime('Conversion Date', readonly=True),
    }

    def init(self, cr):
        tools.drop_view_if_exists(cr, 'crm_opportunity_report')
        cr.execute("""
            CREATE OR REPLACE VIEW crm_opportunity_report AS (
                SELECT
                    c.id,
                    c.date_deadline,

                    c.date_open as opening_date,
                    c.date_closed as date_closed,
                    c.date_last_stage_update as date_last_stage_update,

                    c.user_id,
                    c.probability,
                    c.stage_id,
                    stage.name as stage_name,
                    c.type,
                    c.company_id,
                    c.priority,
                    c.team_id,
                    activity.nbr_activities,
                    c.active,
                    c.campaign_id,
                    c.source_id,
                    c.medium_id,
                    c.partner_id,
                    c.country_id,
                    c.planned_revenue as total_revenue,
                    c.planned_revenue*(c.probability/100) as expected_revenue,
                    c.create_date as create_date,
                    extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as  delay_close,
                    abs(extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24)) as  delay_expected,
                    extract('epoch' from (c.date_open-c.create_date))/(3600*24) as  delay_open,
                    c.lost_reason,
                    c.date_conversion as date_conversion
                FROM
                    "crm_lead" c
                LEFT JOIN (
                    SELECT m.res_id, COUNT(*) nbr_activities
                    FROM "mail_message" m
                    WHERE m.model = 'crm.lead'
                    GROUP BY m.res_id ) activity
                ON
                    (activity.res_id = c.id)
                LEFT JOIN "crm_stage" stage
                ON stage.id = c.stage_id
                GROUP BY c.id, activity.nbr_activities, stage.name
            )""")