bzr revid: mra@mra-laptop-20100702111708-ooy6m8zcy8ztf10i
bzr revid: mra@mra-laptop-20100702123926-t6veq32zgxkw7d8y
bzr revid: mra@mra-laptop-20100702133729-8h08p75fo3ra9mcu
This commit is contained in:
Mustufa Rangwala
2010-07-02 19:07:29 +05:30
7 changed files with 264 additions and 120 deletions
+95 -56
View File
@@ -50,15 +50,17 @@ def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
if not startdate:
startdate = datetime.now()
rset1 = rrule.rrulestr(rrulestring, dtstart=startdate, forceset=True)
if not exdate:
exdate = []
rset1 = rrule.rrulestr(str(rrulestring), dtstart=startdate, forceset=True)
for date in exdate:
datetime_obj = todate(date)
rset1._exdate.append(datetime_obj)
if exrule:
rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate))
re_dates = map(lambda x:x.strftime('%Y-%m-%d %H:%M:%S'), rset1._iter())
return re_dates
return rset1._iter()
def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
"""
@@ -791,61 +793,98 @@ class calendar_alarm(osv.osv):
@param use_new_cursor: False or the dbname
@param context: A standard dictionary for contextual values
"""
if not context:
context = {}
current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
cr.execute("select alarm.id as id \
from calendar_alarm alarm \
where alarm.state = %s and alarm.trigger_date <= %s", ('run', current_datetime))
res = cr.dictfetchall()
alarm_ids = map(lambda x: x['id'], res)
#attendee_obj = self.pool.get('calendar.attendee')
current_datetime = datetime.now()
request_obj = self.pool.get('res.request')
alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context)
mail_to = []
for alarm in self.browse(cr, uid, alarm_ids):
if alarm.action == 'display':
value = {
'name': alarm.name,
'act_from': alarm.user_id.id,
'act_to': alarm.user_id.id,
'body': alarm.description,
'trigger_date': alarm.trigger_date,
'ref_doc1': '%s,%s' % (alarm.model_id.model, alarm.res_id)
}
request_id = request_obj.create(cr, uid, value)
request_ids = [request_id]
for attendee in alarm.attendee_ids:
if attendee.user_id:
value['act_to'] = attendee.user_id.id
request_id = request_obj.create(cr, uid, value)
request_ids.append(request_id)
request_obj.request_send(cr, uid, request_ids)
if alarm.action == 'email':
sub = '[Openobject Remainder] %s' % (alarm.name)
body = """
Name: %s
Date: %s
Description: %s
for alarm in self.browse(cr, uid, alarm_ids, context=context):
next_trigger_date = None
update_vals = {}
model_obj = self.pool.get(alarm.model_id.model)
res_obj = model_obj.browse(cr, uid, alarm.res_id, context=context)
re_dates = []
From:
%s
%s
if res_obj.rrule:
event_date = datetime.strptime(res_obj.date, '%Y-%m-%d %H:%M:%S')
recurrent_dates = get_recurrent_dates(res_obj.rrule, res_obj.exdate, event_date, res_obj.exrule)
""" % (alarm.name, alarm.trigger_date, alarm.description, \
alarm.user_id.name, alarm.user_id.signature)
mail_to = [alarm.user_id.address_id.email]
for att in alarm.attendee_ids:
mail_to.append(att.user_id.address_id.email)
if mail_to:
tools.email_send(
tools.config.get('email_from', False),
mail_to,
sub,
body
)
self.write(cr, uid, [alarm.id], {'state':'done'})
trigger_interval = alarm.trigger_interval
if trigger_interval == 'days':
delta = timedelta(days=alarm.trigger_duration)
if trigger_interval == 'hours':
delta = timedelta(hours=alarm.trigger_duration)
if trigger_interval == 'minutes':
delta = timedelta(minutes=alarm.trigger_duration)
delta = alarm.trigger_occurs == 'after' and delta or -delta
for rdate in recurrent_dates:
if rdate + delta <= current_datetime:
re_dates.append(rdate.strftime("%Y-%m-%d %H:%M:%S"))
rest_dates = recurrent_dates[len(re_dates):]
next_trigger_date = rest_dates and rest_dates[0] or None
else:
re_dates = [alarm.trigger_date]
for r_date in re_dates:
ref = alarm.model_id.model + ',' + str(alarm.res_id)
# search for alreay sent requests
if request_obj.search(cr, uid, [('trigger_date', '=', r_date), ('ref_doc1', '=', ref)], context=context):
continue
if alarm.action == 'display':
value = {
'name': alarm.name,
'act_from': alarm.user_id.id,
'act_to': alarm.user_id.id,
'body': alarm.description,
'trigger_date': r_date,
'ref_doc1': ref
}
request_id = request_obj.create(cr, uid, value)
request_ids = [request_id]
for attendee in res_obj.attendee_ids:
if attendee.user_id:
value['act_to'] = attendee.user_id.id
request_id = request_obj.create(cr, uid, value)
request_ids.append(request_id)
request_obj.request_send(cr, uid, request_ids)
if alarm.action == 'email':
sub = '[Openobject Reminder] %s' % (alarm.name)
body = """
Event: %s
Event Date: %s
Description: %s
From:
%s
----
%s
""" % (alarm.name, alarm.trigger_date, alarm.description, \
alarm.user_id.name, alarm.user_id.signature)
mail_to = [alarm.user_id.address_id.email]
for att in alarm.attendee_ids:
mail_to.append(att.user_id.address_id.email)
if mail_to:
tools.email_send(
tools.config.get('email_from', False),
mail_to,
sub,
body
)
if next_trigger_date:
update_vals.update({'trigger_date': next_trigger_date})
else:
update_vals.update({'state': 'done'})
self.write(cr, uid, [alarm.id], update_vals)
return True
calendar_alarm()
@@ -1273,13 +1312,12 @@ true, it will allow you to hide the event alarm information without removing it.
new_rrule_str.append(new_rule)
new_rrule_str = ';'.join(new_rrule_str)
rdates = get_recurrent_dates(str(new_rrule_str), exdate, start_date, data['exrule'])
for rdate in rdates:
r_date = datetime.strptime(rdate, "%Y-%m-%d %H:%M:%S")
for r_date in rdates:
if start_date and r_date < start_date:
continue
if until_date and r_date > until_date:
continue
idval = real_id2base_calendar_id(data['id'], rdate)
idval = real_id2base_calendar_id(data['id'], r_date.strftime("%Y-%m-%d %H:%M:%S"))
result.append(idval)
count += 1
if result:
@@ -1413,9 +1451,10 @@ true, it will allow you to hide the event alarm information without removing it.
new_ids.append(event_id)
res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context)
if vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'):
if (vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'))\
or (vals.has_key('date') or vals.has_key('duration') or vals.has_key('date_deadline')):
# change alarm details
alarm_obj = self.pool.get('res.alarm')
context.update({'alarm_id': vals.get('alarm_id')})
alarm_obj.do_alarm_create(cr, uid, new_ids, self._name, 'date', \
context=context)
return res
+54 -36
View File
@@ -84,12 +84,12 @@ class marketing_campaign(osv.osv):
_columns = {
'name': fields.char('Name', size=64, required=True),
'object_id': fields.many2one('ir.model', 'Object', required=True,
help="Choose the Object on which you want \
'object_id': fields.many2one('ir.model', 'Model', required=True,
help="Choose the model on which you want \
this campaign to be run"),
'partner_field_id': fields.many2one('ir.model.fields', 'Partner Field',
domain="[('model_id', '=', object_id), ('ttype', '=', 'many2one'), ('relation', '=', 'res.partner')]",
help="The generated workitems will be linked to the partner related to the record"),
help="The generated workitems will be linked to the partner related to the record. If the record is the partner itself left this field empty."),
'mode':fields.selection([('test', 'Test Directly'),
('test_realtime', 'Test in Realtime'),
('manual', 'With Manual Confirmation'),
@@ -115,6 +115,7 @@ you required for the campaign"),
}
def state_running_set(self, cr, uid, ids, *args):
# TODO check that all subcampaigns are running
campaign = self.browse(cr, uid, ids[0])
if not campaign.activity_ids :
raise osv.except_osv("Error", "There is no activitity in the campaign")
@@ -135,6 +136,7 @@ you required for the campaign"),
return True
def state_done_set(self, cr, uid, ids, *args):
# TODO check that this campaign is not a subcampaign in running mode.
segment_ids = self.pool.get('marketing.campaign.segment').search(cr, uid,
[('campaign_id', 'in', ids),
('state', '=', 'running')])
@@ -144,6 +146,7 @@ you required for the campaign"),
return True
def state_cancel_set(self, cr, uid, ids, *args):
# TODO check that this campaign is not a subcampaign in running mode.
self.write(cr, uid, ids, {'state': 'cancelled'})
return True
@@ -265,10 +268,14 @@ class marketing_campaign_segment(osv.osv):
'state': 'todo',
'res_id': o_ids.id
}
partner = None
if partner_field:
partner = getattr(o_ids, partner_field)
if partner:
wi_vals['partner_id'] = partner.id
elif model_obj._name == 'res.partner':
partner = o_ids
if partner:
wi_vals['partner_id'] = partner.id
for act_id in act_ids:
wi_vals['activity_id'] = act_id
@@ -284,8 +291,18 @@ class marketing_campaign_activity(osv.osv):
_name = "marketing.campaign.activity"
_description = "Campaign Activity"
_actions_type = [('email', 'E-mail'), ('paper', 'Paper'), ('action', 'Action'),
('subcampaign', 'Sub-Campaign')]
_action_types = [
('email', 'E-mail'),
('paper', 'Paper'),
('action', 'Action'),
# TODO implement the subcampaigns. The segment_id on woritems must be
# optional (thus campaign_id must be a real many2one, and a constraint
# must be set).
# TODO implement the subcampaign out. disallow out transitions from
# subcampaign activities ?
#('subcampaign', 'Sub-Campaign'),
]
_columns = {
'name': fields.char('Name', size=128, required=True),
'campaign_id': fields.many2one('marketing.campaign', 'Campaign',
@@ -296,8 +313,7 @@ class marketing_campaign_activity(osv.osv):
'start': fields.boolean('Start',help= "This activity is launched when the campaign starts."),
'condition': fields.char('Condition', size=256, required=True,
help="Python condition to know if the activity can be launched"),
'type': fields.selection(_actions_type,
'Type', required=True,
'type': fields.selection(_action_types, 'Type', required=True,
help="Describe type of action to be performed on the Activity.Eg : Send email,Send paper.."),
'email_template_id': fields.many2one('email.template','Email Template'),
'report_id': fields.many2one('ir.actions.report.xml', 'Reports', ),
@@ -312,9 +328,8 @@ class marketing_campaign_activity(osv.osv):
'from_ids': fields.one2many('marketing.campaign.transition',
'activity_to_id',
'Previous Activities'),
'subcampaign_id': fields.many2one('marketing.campaign', 'Sub-Campaign'),
'subcampaign_segment_id': fields.many2one('marketing.campaign.segment',
'Sub Campaign Segment'),
#'subcampaign_id': fields.many2one('marketing.campaign', 'Sub-Campaign',
# domain="[('object_id', '=', object_id)]"),
'variable_cost': fields.float('Variable Cost'),
'revenue': fields.float('Revenue'),
'signal': fields.char('Signal', size=128,
@@ -341,14 +356,6 @@ class marketing_campaign_activity(osv.osv):
),
]
def __init__(self, *args):
# FIXME use self._process_wi_<type>
self._actions = {'paper' : self.process_wi_report,
'email' : self.process_wi_email,
'action' : self.process_wi_action,
}
return super(marketing_campaign_activity, self).__init__(*args)
def search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False):
if context == None:
@@ -363,7 +370,7 @@ class marketing_campaign_activity(osv.osv):
return super(marketing_campaign_activity, self).search(cr, uid, args,
offset, limit, order, context, count)
def process_wi_report(self, cr, uid, activity, workitem, context={}):
def _process_wi_paper(self, cr, uid, activity, workitem, context=None):
service = netsvc.LocalService('report.%s'%activity.report_id.report_name)
(report_data, format) = service.create(cr, uid, [], {}, {})
attach_vals = {
@@ -378,24 +385,36 @@ class marketing_campaign_activity(osv.osv):
self.pool.get('ir.attachment').create(cr, uid, attach_vals)
return True
def process_wi_email(self, cr, uid, activity, workitem, context=None):
def _process_wi_email(self, cr, uid, activity, workitem, context=None):
return self.pool.get('email.template').generate_mail(cr, uid,
activity.email_template_id.id,
[workitem.res_id], context=context)
def process_wi_action(self, cr, uid, activity, workitem, context={}):
context = {}
def _process_wi_action(self, cr, uid, activity, workitem, context=None):
if context is None:
context = {}
server_obj = self.pool.get('ir.actions.server')
context['active_id'] = workitem.res_id
res = server_obj.run(cr, uid, [activity.server_action_id.id], context)
#server action return False if the action is perfomed except client_action,other and python code
return res==False and True or res
def process(self, cr, uid, act_id, wi_id, context={}):
activity = self.browse(cr, uid, act_id)
action_context = dict(context,
active_id=workitem.res_id,
active_ids=[workitem.res_id],
active_model=workitem.object_id.model)
res = server_obj.run(cr, uid, [activity.server_action_id.id],
context=action_context)
# server action return False if the action is perfomed
# except client_action, other and python code
return res == False and True or res
def process(self, cr, uid, act_id, wi_id, context=None):
activity = self.browse(cr, uid, act_id, context=context)
method = '_process_wi_%s' % (activity.type,)
action = getattr(self, method, None)
if not action:
raise NotImplementedError('method %r in not implemented on %r object' % (method, self))
workitem_obj = self.pool.get('marketing.campaign.workitem')
workitem = workitem_obj.browse(cr, uid, wi_id, context=context)
return self._actions[activity.type](cr, uid, activity, workitem, context)
return action(cr, uid, activity, workitem, context)
marketing_campaign_activity()
@@ -587,13 +606,12 @@ class marketing_campaign_workitem(osv.osv):
# test test_realtime manual normal (active)
# time Y N N N
# signal N N N N
# auto Y Y N Y
# auto Y Y Y Y
#
run = False
if transition.trigger != 'signal' and campaign_mode != 'manual':
if transition.trigger == 'auto' or campaign_mode == 'test':
run = True
run = transition.trigger == 'auto' \
or (transition.trigger == 'time' \
and campaign_mode == 'test')
if run:
new_wi = self.browse(cr, uid, wi_id, context)
self._process_one(cr, uid, new_wi, context)
@@ -241,10 +241,11 @@
<group attrs="{'invisible':[('type','!=','action')]}" >
<field name="server_action_id" attrs="{'required':[('type','=','action')]}" domain="[('model_id','=',object_id)]" />
</group>
<!--
<group attrs="{'invisible':[('type','!=','subcampaign')]}" >
<field name="subcampaign_id" attrs="{'required':[('type','=','subcampaign')]}" />
<field name="subcampaign_segment_id" attrs="{'required':[('type','=','subcampaign')]}" />
</group>
-->
</group>
</group>
<newline/>
@@ -22,11 +22,9 @@ import urllib
import urllib2
import simplejson as json
import time
from datetime import datetime
from osv import fields, osv
from marketing_campaign.marketing_campaign import _intervalTypes
mailchimp_url = 'http://%s.api.mailchimp.com/1.2'
class mailchimp_account(osv.osv):
@@ -150,7 +148,7 @@ class marketing_campaign_activity(osv.osv):
return {}
return {'value':{}}
def process_wi_mailchimp(self, cr, uid, activity, workitem, context={}):
def _process_wi_mailchimp(self, cr, uid, activity, workitem, context=None):
mailchimp_account_id = activity.mailchimp_account_id.id
list_name = activity.mailchimp_list
mc_acc_obj = self.pool.get('mailchimp.account')
@@ -193,18 +191,15 @@ class marketing_campaign_activity(osv.osv):
'merge_vars[address][city]' : model_obj.city and model_obj.city or '',
'merge_vars[phone]' : model_obj.phone and model_obj.phone or '',
})
res = mc_acc_obj.get_response(cr, uid, mailchimp_account_id, 'listSubscribe', params)
# handle mailchimp error
mc_acc_obj.get_response(cr, uid, mailchimp_account_id, 'listSubscribe', params)
# TODO handle mailchimp error
return True
else :
else :
return {'error_msg' : "Invalid Email Address"}
def __init__(self, *args):
res = super(marketing_campaign_activity, self).__init__(*args)
self._actions.update({'mailchimp' : self.process_wi_mailchimp})
self._actions_type.append(('mailchimp', 'Mailchimp'))
return res
super(marketing_campaign_activity, self).__init__(*args)
self._action_types.append(('mailchimp', 'Mailchimp'))
marketing_campaign_activity()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+5 -4
View File
@@ -35,12 +35,13 @@
<form string="My Board">
<hpaned>
<child1>
<action colspan="4" height="220" name="%(purchase_draft)d" string="Draft Purchase Order" width="510"/>
<action colspan="4" height="220" name="%(purchase_waiting)d" string="Purchase Order in Waiting" width="510"/>
<action colspan="4" height="220" name="%(purchase.action_current_purchases)d" string="Current purchases" width="510"/>
<action colspan="4" height="220" name="%(purchase.action_po_per_month_tree)d" string="Purchase order per month" width="510"/>
</child1>
<child2>
<action colspan="4" height="220" name="%(purchase.action_purchase_order_report_all)d" string="Total Qty and Amount by month" />
<action colspan="4" height="220" name="%(purchase.action_stock_move_report_po)d" string="Total Orders by User per month" />
<action colspan="4" height="220" name="%(purchase.action_purchase_by_supplier)d" string="Purchase by supplier" />
<action colspan="4" height="220" name="%(purchase.action_total_price_by_product_by_state)d" string="Total price by product by state" />
<action colspan="4" height="220" name="%(purchase.action_qty_per_product)d" string="Total Quantity per product" />
</child2>
</hpaned>
</form>
+8 -10
View File
@@ -34,16 +34,14 @@ class purchase_report(osv.osv):
'date': fields.date('Order Date', readonly=True),
'name': fields.char('Year',size=64,required=False, readonly=True),
'day': fields.char('Day', size=128, readonly=True),
'state': fields.selection([
('draft','Quotation'),
('waiting_date','Waiting Schedule'),
('manual','Manual in progress'),
('progress','In progress'),
('shipping_except','Shipping Exception'),
('invoice_except','Invoice Exception'),
('done','Done'),
('cancel','Cancel')
], 'Order State', readonly=True),
'state': fields.selection([('draft', 'Request for Quotation'),
('wait', 'Waiting'),
('confirmed', 'Waiting Supplier Ack'),
('approved', 'Approved'),
('except_picking', 'Shipping Exception'),
('except_invoice', 'Invoice Exception'),
('done', 'Done'),
('cancel', 'Cancelled')],'Order State', readonly=True),
'product_id':fields.many2one('product.product', 'Product', readonly=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', readonly=True),
'location_id': fields.many2one('stock.location', 'Destination', readonly=True),
@@ -176,6 +176,98 @@
</record>
<menuitem action="action_stock_move_report_po" id="menu_action_stock_move_report_po" parent="base.next_id_73" sequence="8"/>
<record model="ir.ui.view" id="view_purchase_by_supplier_graph">
<field name="name">purchase.by.supplier.graph</field>
<field name="model">purchase.report</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Purchase by supplier" >
<field name="partner_id"/>
<field name="nbr" operator="+"/>
</tree>
</field>
</record>
<record id="action_purchase_by_supplier" model="ir.actions.act_window">
<field name="name">Purchase by supplier</field>
<field name="res_model">purchase.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="domain">[('state','=','done'),('date','&gt;',(datetime.date.today()-datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]</field>
<field name="context">{'group_by_no_leaf':1,'group_by':['partner_id']}</field>
<field name="view_id" ref="view_purchase_by_supplier_graph"/>
</record>
<record model="ir.ui.view" id="view_total_price_by_product_by_state_graph">
<field name="name">total.price.by.product.by.state.graph</field>
<field name="model">purchase.report</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Total price by product by state" >
<field name="product_id"/>
<field name="price_total" operator="+"/>
<field name="state" group="True"/>
</tree>
</field>
</record>
<record id="action_total_price_by_product_by_state" model="ir.actions.act_window">
<field name="name">Total price by product by state</field>
<field name="res_model">purchase.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="context">{'group_by_no_leaf':1,'group_by':['product_id','state']}</field>
<field name="view_id" ref="view_total_price_by_product_by_state_graph"/>
</record>
<record model="ir.ui.view" id="view_qty_per_product_graph">
<field name="name">qty.per.product.graph</field>
<field name="model">purchase.report</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Qty. per product" >
<field name="product_id"/>
<field name="quantity" operator="+"/>
</tree>
</field>
</record>
<record id="action_qty_per_product" model="ir.actions.act_window">
<field name="name">Qty. per product</field>
<field name="res_model">purchase.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="domain">[('state','=','done')]</field>
<field name="context">{'group_by_no_leaf':1,'group_by':['product_id']}</field>
<field name="view_id" ref="view_qty_per_product_graph"/>
</record>
<record model="ir.ui.view" id="view_po_per_month_tree">
<field name="name">qty.per.product.tree</field>
<field name="model">purchase.report</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Purchase order per month" >
<field name="month"/>
<field name="product_id" invisible="1"/>
<field name="nbr" operator="+"/>
</tree>
</field>
</record>
<record id="action_po_per_month_tree" model="ir.actions.act_window">
<field name="name">Purchase order per month</field>
<field name="res_model">purchase.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
<field name="domain">[('date','&gt;',time.strftime('%Y-01-01 00:00:00')),('date','&lt;',time.strftime('%Y-12-31 23:59:59')),('state','=','done')]</field>
<field name="context">{'group_by_no_leaf':1,'group_by':['month','product_id']}</field>
<field name="view_id" ref="view_po_per_month_tree"/>
</record>
<record id="action_current_purchases" model="ir.actions.act_window">
<field name="name">Current purchases</field>
<field name="res_model">purchase.order</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('state','in',('draft','confirmed'))]</field>
<field name="view_id" ref="purchase.purchase_order_tree"/>
</record>
</data>
</openerp>