1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # POS Membership module for OpenERP, Manage membership payments from POS.
5 # Copyright (C) 2013 L'Heureux Cyclage (<http://www.heureux-cyclage.org>)
7 # This file is a part of POS Membership
9 # POS Membership is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # POS Membership is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 ##############################################################################
23 from openerp
import netsvc
24 from openerp
.osv
import fields
, osv
25 from openerp
.tools
.translate
import _
27 from datetime
import datetime
, date
28 from dateutil
.relativedelta
import relativedelta
32 ('none', 'Non Member'),
33 ('canceled', 'Cancelled Member'),
34 ('old', 'Old Member'),
35 ('waiting', 'Waiting Member'),
36 ('invoiced', 'Invoiced Member'),
37 ('free', 'Free Member'),
38 ('paid', 'Paid Member'),
42 class pos_session(osv
.osv
):
43 _inherit
= 'pos.session'
45 def open_cb(self
, cr
, uid
, ids
, context
=None):
47 Avoid call the Point Of Sale interface and set the pos.session to 'opened' (in progress)
52 if isinstance(ids
, (int, long)):
55 this_record
= self
.browse(cr
, uid
, ids
[0], context
=context
)
56 this_record
._workflow
_signal
('open')
58 context
.update(active_id
=this_record
.id)
63 def wkf_action_close(self
, cr
, uid
, ids
, context
=None):
64 # Check that a register is not closed with a zero balance if it started with a positive balance
65 for record
in self
.browse(cr
, uid
, ids
, context
=context
):
66 for st
in record
.statement_ids
:
68 if st
.balance_start
>0 and not st
.balance_end_real
:
69 # The pos manager can close sessions with 0 for the end balance.
70 if not self
.pool
.get('ir.model.access').check_groups(cr
, uid
, "point_of_sale.group_pos_manager"):
71 raise osv
.except_osv( _('Please check your ending balance!'),
72 _("Your ending balance is zero, don't forget to enter it before closing the session! Leave around 20-30 EUR in the register."))
73 return super(pos_session
, self
).wkf_action_close(cr
, uid
, ids
, context
)
79 class pos_order(osv
.osv
):
80 _inherit
= 'pos.order'
82 def create_partner_from_ui(self
, cr
, uid
, partners
, context
=None):
83 print ("DEV: [pos_membership] [create_partner_from_ui] partners=%s" % str(partners
))
85 partner_obj
= self
.pool
.get('res.partner')
86 for tmp_partner
in partners
:
87 partner
= tmp_partner
['data']
88 partner_id
= partner_obj
.create(cr
, uid
, {
89 'name': partner
['name'],
91 partner_ids
.append(partner_id
)
92 #self.signal_paid(cr, uid, [partner_id])
95 def create_from_ui(self
, cr
, uid
, orders
, context
=None):
96 # NOTE: copied from openerp/addons/point_of_sale/point_of_sale.py
97 #_logger.info("orders: %r", orders)
98 print ("DEV: [pos_membership] [create_from_ui] order=", str(orders
))
100 for tmp_order
in orders
:
101 order
= tmp_order
['data']
102 order_id
= self
.create(cr
, uid
, {
103 'name': order
['name'],
104 'user_id': order
['user_id'] or False,
105 'session_id': order
['pos_session_id'],
106 'lines': order
['lines'],
107 'pos_reference': order
['name'],
108 #-- BEGIN pos_membership
109 'partner_id': order
['partner_id'],
110 #-- END pos_membership
113 for payments
in order
['statement_ids']:
114 payment
= payments
[2]
115 self
.add_payment(cr
, uid
, order_id
, {
116 'amount': payment
['amount'] or 0.0,
117 'payment_date': payment
['name'],
118 'statement_id': payment
['statement_id'],
119 'payment_name': payment
.get('note', False),
120 'journal': payment
['journal_id']
123 if order
['amount_return']:
124 session
= self
.pool
.get('pos.session').browse(cr
, uid
, order
['pos_session_id'], context
=context
)
125 cash_journal
= session
.cash_journal_id
126 cash_statement
= False
128 cash_journal_ids
= filter(lambda st
: st
.journal_id
.type == 'cash', session
.statement_ids
)
129 if not len(cash_journal_ids
):
130 raise osv
.except_osv(_('error!'),
131 _("No cash statement found for this session. Unable to record returned cash."))
132 cash_journal
= cash_journal_ids
[0].journal_id
133 self
.add_payment(cr
, uid
, order_id
, {
134 'amount': -order
['amount_return'],
135 'payment_date': time
.strftime('%Y-%m-%d %H:%M:%S'),
136 'payment_name': _('return'),
137 'journal': cash_journal
.id,
139 order_ids
.append(order_id
)
140 wf_service
= netsvc
.LocalService("workflow")
141 wf_service
.trg_validate(uid
, 'pos.order', order_id
, 'paid', cr
)
147 class membership_line(osv
.osv
):
149 _inherit
= 'membership.membership_line'
151 def _get_partners(self
, cr
, uid
, ids
, context
=None):
152 list_membership_line
= []
153 member_line_obj
= self
.pool
.get('membership.membership_line')
154 for partner
in self
.pool
.get('res.partner').browse(cr
, uid
, ids
, context
=context
):
155 if partner
.member_lines
:
156 list_membership_line
+= member_line_obj
.search(cr
, uid
, [('id', 'in', [l
.id for l
in partner
.member_lines
])], context
=context
)
157 print("DEV: [pos_membership] [membership_line] [_get_partners]: ids=%s res=%s" % (str(ids
), str(list_membership_line
)))
158 return list_membership_line
160 #def _get_membership_lines_from_account_invoice(self, cr, uid, ids, context=None):
161 # list_membership_line = []
162 # member_line_obj = self.pool.get('membership.membership_line')
163 # for invoice in self.pool.get('account.invoice').browse(cr, uid, ids, context=context):
164 # if invoice.invoice_line:
165 # list_membership_line += member_line_obj.search(cr, uid, [('account_invoice_line', 'in', [ l.id for l in invoice.invoice_line])], context=context)
167 # { 'class': 'account.invoice'
168 # , 'ids': list_membership_line
170 # #res= list_membership_line
171 # print("DEV: [pos_membership] [membership_line] [_get_membership_lines_from_account_invoice]: res=%s" % str(res))
173 def _get_membership_lines_from_pos_order(self
, cr
, uid
, ids
, context
=None):
174 list_membership_line
= []
175 member_line_obj
= self
.pool
.get('membership.membership_line')
176 print("DEV: [pos_membership] [membership_line] [_get_membership_lines_from_pos_order]: ids=%s context=%s" % (str(ids
), str(context
)))
177 for order
in self
.pool
.get('pos.order').browse(cr
, uid
, ids
, context
=context
):
179 list_membership_line
+= member_line_obj
.search(cr
, uid
, [('pos_order_line', 'in', [l
.id for l
in order
.lines
])], context
=context
)
181 # { 'class': 'pos.order'
182 # , 'ids': list_membership_line
184 res
= list_membership_line
185 print("DEV: [pos_membership] [membership_line] [_get_membership_lines_from_pos_order]: res=%s" % str(res
))
188 def _check_membership_date(self
, cr
, uid
, ids
, context
=None):
189 """Check if membership product is not in the past
190 @param self: The object pointer
191 @param cr: the current row, from the database cursor,
192 @param uid: the current user’s ID for security checks,
193 @param ids: List of Membership Line IDs
194 @param context: A standard dictionary for contextual values
198 SELECT MIN(ml.date_to - ai.date_invoice)
199 FROM membership_membership_line ml
200 JOIN account_invoice_line ail ON (
201 ml.account_invoice_line = ail.id
203 JOIN account_invoice ai ON (
204 ai.id = ail.invoice_id)
205 WHERE ml.id IN %s''', (tuple(ids
),))
208 if r
[0] and r
[0] < 0:
211 SELECT MIN(ml.date_to - ai.date_order)
212 FROM membership_membership_line ml
213 JOIN pos_order_line ail ON (
214 ml.pos_order_line = ail.id
216 JOIN pos_order ai ON (
217 ai.id = ail.order_id)
218 WHERE ml.id IN %s''', (tuple(ids
),))
221 if r
[0] and r
[0] < 0:
225 def _state(self
, cr
, uid
, ids
, name
, args
, context
=None):
226 """Compute the state lines
227 @param self: The object pointer
228 @param cr: the current row, from the database cursor,
229 @param uid: the current user’s ID for security checks,
230 @param ids: List of Membership Line IDs
231 @param name: Field Name
232 @param context: A standard dictionary for contextual values
233 @param return: Dictionary of state Value
236 print("DEV: [pos_membership] [membership_line] [_state]: name=%s ids=%s args=%s context=%s" % (str(name
), str(ids
), str(args
), str(context
)))
237 inv_obj
= self
.pool
.get('account.invoice')
238 ord_obj
= self
.pool
.get('pos.order')
239 for line
in self
.browse(cr
, uid
, ids
, context
=context
):
241 SELECT i.state, i.id FROM
245 SELECT l.invoice_id FROM
246 account_invoice_line l WHERE
248 SELECT ml.account_invoice_line FROM
249 membership_membership_line ml WHERE
254 fetched
= cr
.fetchone()
258 if (istate
== 'draft') |
(istate
== 'proforma'):
260 elif istate
== 'open':
262 elif istate
== 'paid':
264 inv
= inv_obj
.browse(cr
, uid
, fetched
[1], context
=context
)
265 for payment
in inv
.payment_ids
:
266 if payment
.invoice
and payment
.invoice
.type == 'out_refund':
268 elif istate
== 'cancel':
273 SELECT i.state, i.id FROM
277 SELECT l.order_id FROM
278 pos_order_line l WHERE
280 SELECT ml.pos_order_line FROM
281 membership_membership_line ml WHERE
286 fetched
= cr
.fetchone()
288 res
[line
.id] = 'canceled'
290 print("DEV: [pos_membership] [membership_line] [_state]: line.partner.id=%s" % (str(line
.partner
.id)))
291 self
.write(cr
, uid
, line
.id, {'partner': line
.partner
.id})
292 # NOTE: force une mise à jour du partner,
293 # car il n'y en a pas lors de la création du pos.order
294 # dans le PoS (pour le moment)
295 partner_obj
= self
.pool
.get('res.partner')
296 for partner
in partner_obj
.browse(cr
, uid
, [line
.partner
.id], context
=context
):
297 if not partner
.member_ident
:
298 mbr_id
= self
.pool
.get('ir.sequence').get(cr
, uid
, 'member_ident')
299 partner_obj
.write(cr
, uid
, partner
.id, {'member_ident': mbr_id
})
302 if (ostate
== 'paid') |
(ostate
== 'draft'):
304 elif (ostate
== 'done') |
(ostate
== 'invoiced'):
306 # XXX: regarder l'équivalent de out_refund pour un pos.order
307 print("DEV: [pos_membership] [membership_line] [_state]: paid: TODO")
308 #inv = ord_obj.browse(cr, uid, fetched[1], context=context)
309 #for payment in inv.payment_ids:
310 # if payment.invoice and payment.invoice.type == 'out_refund':
312 elif ostate
== 'cancel':
315 print("DEV: [pos_membership] [membership_line] [_state]: return=%s" % (str(res
)))
318 def write(self
, cr
, uid
, ids
, vals
, context
=None):
319 print("DEV: [pos_membership] [membership_line] [write]: ids=%s vals=%s" % (str(ids
), str(vals
)))
320 res
= super(membership_line
, self
).write(cr
, uid
, ids
, vals
, context
=context
)
321 print("DEV: [pos_membership] [membership_line] [write]: res=%s" % str(res
))
324 def create(self
, cr
, uid
, vals
, context
=None):
325 print("DEV: [pos_membership] [membership_line] [create]: vals=%s" % (str(vals
)))
326 member_line_obj
= self
.pool
.get('membership.membership_line')
327 res
= super(membership_line
, self
).create(cr
, uid
, vals
, context
=context
)
328 print("DEV: [pos_membership] [membership_line] [create]: res=%s" % (str(res
)))
332 'pos_order_line': fields
.many2one('pos.order.line', 'POS Order line', readonly
=True),
333 'pos_order_id': fields
.related('pos_order_line', 'order_id', type='many2one', relation
='pos.order', string
='Order', readonly
=True),
334 'state': fields
.function(_state
,
335 string
='Membership Status', type='selection',
338 #'account.invoice': (_get_membership_lines_from_account_invoice, ['state'], 10),
339 # NOTE: déjà géré par membership.membership_line._get_membership_lines
340 'pos.order': (_get_membership_lines_from_pos_order
, ['state', 'partner_id'], 10),
341 'res.partner': (_get_partners
, ['membership_state'], 12),
342 }, help="""It indicates the membership status.
343 -Non Member: A member who has not applied for any membership.
344 -Cancelled Member: A member who has cancelled his membership.
345 -Old Member: A member whose membership date has expired.
346 -Waiting Member: A member who has applied for the membership and whose invoice is going to be created.
347 -Invoiced Member: A member whose invoice has been created.
348 -Paid Member: A member who has paid the membership amount."""),
349 'company_id': fields
.related('account_invoice_line', 'invoice_id', 'company_id', type="many2one", relation
="res.company", string
="Company", readonly
=True, store
=True)
350 # XXX: pos_order_line a aussi un company_id
356 class Partner(osv
.osv
):
358 _inherit
= 'res.partner'
360 def _get_partner_id(self
, cr
, uid
, ids
, context
=None):
361 print("DEV: [pos_membership] [Partner] [_get_partner_id]: ids=%s context=%s" % (str(ids
), str(context
)))
362 member_line_obj
= self
.pool
.get('membership.membership_line')
363 res_obj
= self
.pool
.get('res.partner')
364 data_inv
= member_line_obj
.browse(cr
, uid
, ids
, context
=context
)
366 for data
in data_inv
:
367 list_partner
.append(data
.partner
.id)
370 ids2
= res_obj
.search(cr
, uid
, [('associate_member', 'in', ids2
)], context
=context
)
372 print("DEV: [pos_membership] [Partner] [_get_partner_id]: res=%s" % (str(list_partner
)))
375 def _get_invoice_partner(self
, cr
, uid
, ids
, context
=None):
376 inv_obj
= self
.pool
.get('account.invoice')
377 res_obj
= self
.pool
.get('res.partner')
378 data_inv
= inv_obj
.browse(cr
, uid
, ids
, context
=context
)
380 for data
in data_inv
:
381 list_partner
.append(data
.partner_id
.id)
384 ids2
= res_obj
.search(cr
, uid
, [('associate_member', 'in', ids2
)], context
=context
)
388 def _get_order_partner(self
, cr
, uid
, ids
, context
=None):
389 ord_obj
= self
.pool
.get('pos.order')
390 res_obj
= self
.pool
.get('res.partner')
391 data_ord
= ord_obj
.browse(cr
, uid
, ids
, context
=context
)
393 for data
in data_ord
:
394 list_partner
.append(data
.partner_id
.id)
397 ids2
= res_obj
.search(cr
, uid
, [('associate_member', 'in', ids2
)], context
=context
)
401 def _membership_state(self
, cr
, uid
, ids
, name
, args
, context
=None):
402 """This Function return Membership State For Given Partner.
403 @param self: The object pointer
404 @param cr: the current row, from the database cursor,
405 @param uid: the current user’s ID for security checks,
406 @param ids: List of Partner IDs
407 @param name: Field Name
408 @param context: A standard dictionary for contextual values
409 @param return: Dictionary of Membership state Value
412 print("DEV: [pos_membership] [Partner] [_membership_state]: name=%s ids=%s args=%s context=%s" % (str(name
), str(ids
), str(args
), str(context
)))
415 today
= time
.strftime('%Y-%m-%d')
417 print("DEV: [pos_membership] [Partner] [_membership_state] id=%s" % (str(id)))
418 partner_data
= self
.browse(cr
, uid
, id, context
=context
)
419 if partner_data
.membership_cancel
and today
> partner_data
.membership_cancel
:
422 if partner_data
.membership_stop
and today
> partner_data
.membership_stop
:
426 print("DEV: [pos_membership] [Partner] [_membership_state] [partner_data] [member_lines] [test]")
427 if partner_data
.member_lines
:
428 print("DEV: [pos_membership] [Partner] [_membership_state] [partner_data] [member_lines]" % ())
429 for mline
in partner_data
.member_lines
:
430 print("DEV: [pos_membership] [Partner] [_membership_state] [mline]: id=%s" % str(mline
.id))
431 if mline
.date_to
>= today
:
432 if mline
.account_invoice_line
and mline
.account_invoice_line
.invoice_id
:
433 istate
= mline
.account_invoice_line
.invoice_id
.state
434 print("DEV: [pos_membership] [Partner] [_membership_state] [account_invoice_line]: id=%s istate=%s" % (str(mline
.account_invoice_line
.invoice_id
.id), str(istate
)))
437 inv
= mline
.account_invoice_line
.invoice_id
438 for payment
in inv
.payment_ids
:
439 if payment
.invoice
.type == 'out_refund':
442 elif istate
== 'open' and s
!= 0:
444 elif istate
== 'cancel' and s
!= 0 and s
!= 1:
446 elif (istate
== 'draft' or istate
== 'proforma') and s
!= 0 and s
!= 1:
448 elif mline
.pos_order_line
and mline
.pos_order_line
.order_id
:
449 ostate
= mline
.pos_order_line
.order_id
.state
450 print("DEV: [pos_membership] [Partner] [_membership_state] [pos_order_line]: id=%s ostate=%s" % (str(mline
.pos_order_line
.order_id
.id), str(ostate
)))
451 if ostate
== 'paid' and s
!= 0 and s
!= 1:
453 if ostate
== 'invoiced':
457 # XXX: regarder l'équivalent de out_refund pour un pos.order
458 print("DEV: [pos_membership] [partner] [_membership_state]: paid: TODO")
459 #inv = mline.pos_order_line.order_id
460 #for payment in inv.payment_ids:
461 # if payment.invoice.type == 'out_refund':
464 elif ostate
== 'open' and s
!= 0:
465 # XXX: 1 donne invoiced, c'est pitet pas bon
466 print("DEV: [pos_membership] [partner] [_membership_state]: invoiced: TODO")
468 elif ostate
== 'cancel' and s
!= 0 and s
!= 1:
470 elif ostate
== 'draft' and s
!= 0 and s
!= 1:
473 # when we don't have an invoice line only use the date to get the state.
477 for mline
in partner_data
.member_lines
:
478 if mline
.date_from
< today
and \
479 mline
.date_to
< today
and \
480 mline
.date_from
<= mline
.date_to
and \
481 ((mline
.account_invoice_line
and mline
.account_invoice_line
.invoice_id
.state
) == 'paid' or \
482 (mline
.pos_order_line
and \
483 (mline
.pos_order_line
.order_id
.state
== 'paid' or \
484 mline
.pos_order_line
.order_id
.state
== 'done' or \
485 mline
.pos_order_line
.order_id
.state
== 'invoiced' ))):
501 if partner_data
.free_member
and s
!= 0:
503 if partner_data
.associate_member
:
504 res_state
= self
._membership
_state
(cr
, uid
, [partner_data
.associate_member
.id], name
, args
, context
=context
)
505 res
[id] = res_state
[partner_data
.associate_member
.id]
506 print("DEV: [pos_membership] [Partner] [_membership_state]: res=%s" % (str(res
)))
509 def _membership_date(self
, cr
, uid
, ids
, name
, args
, context
=None):
510 """Return date of membership"""
513 member_line_obj
= self
.pool
.get('membership.membership_line')
514 print("DEV: [pos_membership] [Partner] [_membership_date]: ids=%s" % (str(ids
)))
515 for partner
in self
.browse(cr
, uid
, ids
, context
=context
):
516 if partner
.associate_member
:
517 partner_id
= partner
.associate_member
.id
519 partner_id
= partner
.id
521 'membership_start': False,
522 'membership_stop': False,
523 'membership_cancel': False
525 if name
== 'membership_start':
526 line_id
= member_line_obj
.search(cr
, uid
, [('partner', '=', partner_id
),('date_cancel','=',False)],
527 limit
=1, order
='date_from', context
=context
)
529 res
[partner
.id]['membership_start'] = member_line_obj
.read(cr
, uid
, line_id
[0],
530 ['date_from'], context
=context
)['date_from']
531 print("DEV: [pos_membership] [Partner] [_membership_date] [membership_start]: ids=%s date=%s" % (str(ids
), str(res
[partner
.id]['membership_start'])))
533 if name
== 'membership_stop':
534 line_id1
= member_line_obj
.search(cr
, uid
, [('partner', '=', partner_id
),('date_cancel','=',False)],
535 limit
=1, order
='date_to desc', context
=context
)
537 res
[partner
.id]['membership_stop'] = member_line_obj
.read(cr
, uid
, line_id1
[0],
538 ['date_to'], context
=context
)['date_to']
539 print("DEV: [pos_membership] [Partner] [_membership_date] [membership_stop]: ids=%s date=%s" % (str(ids
), str(res
[partner
.id]['membership_stop'])))
541 if name
== 'membership_cancel':
542 if partner
.membership_state
== 'canceled':
543 line_id2
= member_line_obj
.search(cr
, uid
, [('partner', '=', partner
.id)], limit
=1, order
='date_cancel', context
=context
)
545 res
[partner
.id]['membership_cancel'] = member_line_obj
.read(cr
, uid
, line_id2
[0], ['date_cancel'], context
=context
)['date_cancel']
546 print("DEV: [pos_membership] [Partner] [_membership_date] [membership_cancel]: ids=%s date=%s" % (str(ids
), str(res
[partner
.id]['membership_cancel'])))
547 print("DEV: [pos_membership] [Partner] [_membership_date]: res=%s" % (str(res
)))
550 def _get_partners(self
, cr
, uid
, ids
, context
=None):
553 ids2
= self
.search(cr
, uid
, [('associate_member', 'in', ids2
)], context
=context
)
557 def __get_membership_state(self
, *args
, **kwargs
):
558 return self
._membership
_state
(*args
, **kwargs
)
561 'membership_state': fields
.function(
562 __get_membership_state
,
563 string
= 'Current Membership Status', type = 'selection',
566 # NOTE: il est important que la priorité soit plus grande
567 # que les membership_{start,stop,cancel}
568 # car _membership_state s'en sert et doit donc les trouver à jour.
569 'account.invoice': (_get_invoice_partner
, ['state'], 20),
570 'pos.order': (_get_order_partner
, ['state', 'partner_id'], 20),
571 'membership.membership_line': (_get_partner_id
, ['state'], 20),
572 'res.partner': (_get_partners
, ['free_member', 'membership_state', 'associate_member'], 20)
573 }, help="""It indicates the membership state.
574 -Non Member: A partner who has not applied for any membership.
575 -Cancelled Member: A member who has cancelled his membership.
576 -Old Member: A member whose membership date has expired.
577 -Waiting Member: A member who has applied for the membership and whose invoice is going to be created.
578 -Invoiced Member: A member whose invoice has been created.
579 -Paying member: A member who has paid the membership fee."""),
580 'membership_start': fields
.function(
581 _membership_date
, multi
= 'membership_start',
582 string
= 'Membership Start Date', type = 'date',
584 'account.invoice': (_get_invoice_partner
, ['state'], 10),
585 'pos.order': (_get_order_partner
, ['state', 'partner_id'], 10),
586 'membership.membership_line': (_get_partner_id
, ['state'], 10),
587 'res.partner': (lambda self
, cr
, uid
, ids
, c
={}: ids
, ['free_member'], 10)
588 }, help="Date from which membership becomes active."),
589 'membership_stop': fields
.function(
591 string
= 'Membership End Date', type='date', multi
='membership_stop',
593 'account.invoice': (_get_invoice_partner
, ['state'], 10),
594 'pos.order': (_get_order_partner
, ['state', 'partner_id'], 10),
595 'membership.membership_line': (_get_partner_id
, ['state'], 10),
596 'res.partner': (lambda self
, cr
, uid
, ids
, c
={}: ids
, ['free_member'], 10)
597 }, help="Date until which membership remains active."),
598 'membership_cancel': fields
.function(
600 string
= 'Cancel Membership Date', type='date', multi
='membership_cancel',
602 'account.invoice': (_get_invoice_partner
, ['state'], 11),
603 'pos.order': (_get_order_partner
, ['state', 'partner_id'], 11),
604 'membership.membership_line': (_get_partner_id
, ['state'], 10),
605 'res.partner': (lambda self
, cr
, uid
, ids
, c
={}: ids
, ['free_member'], 10)
606 }, help="Date on which membership has been cancelled"),
611 class pos_order(osv
.osv
):
612 _inherit
= 'pos.order'
614 # XXX: copied from openerp/addons/point_of_sale/point_of_sale.py
615 def action_invoice(self
, cr
, uid
, ids
, context
=None):
616 print ("DEV: [pos_membership] [pos_order] [action_invoice]")
617 wf_service
= netsvc
.LocalService("workflow")
618 inv_ref
= self
.pool
.get('account.invoice')
619 inv_line_ref
= self
.pool
.get('account.invoice.line')
620 product_obj
= self
.pool
.get('product.product')
623 for order
in self
.pool
.get('pos.order').browse(cr
, uid
, ids
, context
=context
):
625 inv_ids
.append(order
.invoice_id
.id)
628 if not order
.partner_id
:
629 raise osv
.except_osv(_('Error!'), _('Please provide a partner for the sale.'))
631 acc
= order
.partner_id
.property_account_receivable
.id
632 # XXX: copied from openerp/addons/point_of_sale/point_of_sale.py to fix account.invoice.line creation
635 'origin': order
.name
,
637 'journal_id': order
.sale_journal
.id or None,
638 'type': 'out_invoice',
639 'reference': order
.name
,
640 'partner_id': order
.partner_id
.id,
641 'comment': order
.note
or '',
642 'currency_id': order
.pricelist_id
.currency_id
.id, # considering partner's sale pricelist's currency
645 inv
.update(inv_ref
.onchange_partner_id(cr
, uid
, [], 'out_invoice', order
.partner_id
.id)['value'])
646 if not inv
.get('account_id', None):
647 inv
['account_id'] = acc
648 for line
in order
.lines
:
650 #'invoice_id': inv_id,
651 'product_id': line
.product_id
.id,
652 'quantity': line
.qty
,
654 inv_name
= product_obj
.name_get(cr
, uid
, [line
.product_id
.id], context
=context
)[0][1]
655 inv_line
.update(inv_line_ref
.product_id_change(cr
, uid
, [],
657 line
.product_id
.uom_id
.id,
658 line
.qty
, partner_id
= order
.partner_id
.id,
659 fposition_id
=order
.partner_id
.property_account_position
.id)['value'])
660 if line
.product_id
.description_sale
:
661 inv_line
['note'] = line
.product_id
.description_sale
662 inv_line
['price_unit'] = line
.price_unit
663 inv_line
['discount'] = line
.discount
664 inv_line
['name'] = inv_name
665 inv_line
['invoice_line_tax_id'] = [(6, 0, [x
.id for x
in line
.product_id
.taxes_id
] )]
666 inv_line_ref
.create(cr
, uid
, inv_line
, context
=context
)
667 #inv_line_ref.create(cr, uid, inv_line, context=context)
668 inv
['invoice_line'].append((0, 0, inv_line
))
669 inv_id
= inv_ref
.create(cr
, uid
, inv
, context
=context
)
671 self
.write(cr
, uid
, [order
.id], {'invoice_id': inv_id
, 'state': 'invoiced'}, context
=context
)
672 inv_ref
.confirm_paid(cr
, uid
, [inv_id
], context
=context
)
673 inv_ids
.append(inv_id
)
674 inv_ref
.button_reset_taxes(cr
, uid
, [inv_id
], context
=context
)
675 wf_service
.trg_validate(uid
, 'pos.order', order
.id, 'invoice', cr
)
677 if not inv_ids
: return {}
679 mod_obj
= self
.pool
.get('ir.model.data')
680 res
= mod_obj
.get_object_reference(cr
, uid
, 'account', 'invoice_form')
681 res_id
= res
and res
[1] or False
683 'name': _('Customer Invoice'),
687 'res_model': 'account.invoice',
688 'context': "{'type':'out_invoice'}",
689 'type': 'ir.actions.act_window',
692 'res_id': inv_ids
and inv_ids
[0] or False,
695 def write(self
, cr
, uid
, ids
, vals
, context
=None):
696 print("DEV: [pos_membership] [pos_order] [write]: ids=%s vals=%s" % (str(ids
), str(vals
)))
697 pos_order_obj
= self
.pool
.get('pos.order')
698 res
= super(pos_order
, self
).write(cr
, uid
, ids
, vals
, context
=context
)
699 print("DEV: [pos_membership] [pos_order] [write]: res=%s" % str(res
))
702 def create(self
, cr
, uid
, vals
, context
=None):
703 print("DEV: [pos_membership] [pos_order] [create]: vals=%s" % (str(vals
)))
704 pos_order_obj
= self
.pool
.get('pos.order')
705 res
= super(pos_order
, self
).create(cr
, uid
, vals
, context
=context
)
706 print("DEV: [pos_membership] [pos_order] [create]: res=%s" % (str(res
)))
711 class pos_order_line(osv
.osv
):
712 _inherit
= 'pos.order.line'
714 def write(self
, cr
, uid
, ids
, vals
, context
=None):
715 print("DEV: [pos_membership] [pos_order_line] [write]: ids=%s vals=%s context=%s" % (str(ids
), str(vals
), str(context
)))
716 member_line_obj
= self
.pool
.get('membership.membership_line')
717 res
= super(pos_order_line
, self
).write(cr
, uid
, ids
, vals
, context
=context
)
718 print ("DEV: [pos_membership] [pos_order_line] [write] [super] : res=%s" % str(res
))
719 for line
in self
.browse(cr
, uid
, ids
, context
=context
):
720 ml_ids
= member_line_obj
.search(cr
, uid
, [('pos_order_line', '=', line
.id)], context
=context
)
721 if line
.product_id
and line
.product_id
.membership
:
722 member_line_id
= member_line_obj
.search(cr
, uid
723 , [('partner', '=', line
.order_id
.partner_id
.id)
724 ,('pos_order_line', '=', line
.id)]
727 for member_line
in member_line_obj
.browse(cr
, uid
, member_line_id
, context
=context
):
728 # NOTE: get member_line created in pos_membership.pos_order_line.create
729 date_from
= member_line
.date_from
730 date_to
= member_line
.date_to
731 if line
.product_id
.membership_date2date
:
732 date_from
= ('date_from' in context
733 and context
.get('date_from')
734 and datetime
.strptime(context('date_from'), "%Y-%m-%d")
736 date_to
= date_from
+ relativedelta(months
= +12) # TODO: parameterize this delta?
737 date_from
= date_from
.strftime("%Y-%m-%d")
738 date_to
= date_to
.strftime("%Y-%m-%d")
739 print ("DEV: [pos_membership] [pos_order_line] [write] date_from: %s" % str(date_from
))
740 print ("DEV: [pos_membership] [pos_order_line] [write] date_to : %s" % str(date_to
))
741 member_line_obj
.write(cr
, uid
, member_line
.id
742 , {'date_from': date_from
746 if line
.product_id
.membership_grouped
:
747 if line
.order_id
.partner_id
.associate_members
:
748 associate_member_line_ids
= member_line_obj
.search(cr
, uid
749 , [ ('pos_order_line', '=', line
.id)
750 , ('partner', '!=', line
.order_id
.partner_id
.id)
753 for associate_member_line
in member_line_obj
.browse(cr
, uid
, associate_member_line_ids
, context
=context
):
754 print ("DEV: [pos_membership] [pos_order_line] [write] [associate] date_from: %s" % str(date_from
))
755 print ("DEV: [pos_membership] [pos_order_line] [write] [associate] date_to : %s" % str(date_to
))
756 member_line_obj
.write(cr
, uid
, associate_member_line
.id
757 , {'date_from': date_from
762 print("DEV: mettre une contrainte pour l'objet\
763 membership.membership_line interdisant les\
764 adhésions groupées reliées à des partenaires\
765 sans membres associés")
767 associate_member_line_ids
= member_line_obj
.search(cr
, uid
768 , [ ('pos_order_line', '=', line
.id)
769 , ('partner', '!=', line
.order_id
.partner_id
.id)
772 member_line_obj
.unlink(cr
, uid
, associate_member_line_ids
, context
=context
)
773 #Define member ident if it's necessary
774 partners
= [line
.order_id
.partner_id
]
775 if line
.order_id
.partner_id
.associate_members
:
776 partners
.extend(line
.order_id
.partner_id
.associate_members
)
778 if not i
.member_ident
:
779 mbr_id
= self
.pool
.get('ir.sequence').get(cr
, uid
, 'member_ident')
780 self
.pool
.get('res.partner').write(cr
, uid
, i
.id, {'member_ident': mbr_id
})
781 if line
.product_id
and not line
.product_id
.membership
and ml_ids
:
782 # Product line has changed to a non membership product
783 member_line_obj
.unlink(cr
, uid
, ml_ids
, context
=context
)
784 print ("DEV: [pos_membership] [pos_order_line] [write] : return=%s" % str(res
))
787 def unlink(self
, cr
, uid
, ids
, context
=None):
788 """Remove Membership Line Record for Account Invoice Line
790 member_line_obj
= self
.pool
.get('membership.membership_line')
792 ml_ids
= member_line_obj
.search(cr
, uid
, [('pos_order_line', '=', id)], context
=context
)
793 member_line_obj
.unlink(cr
, uid
, ml_ids
, context
=context
)
794 return super(pos_order_line
, self
).unlink(cr
, uid
, ids
, context
=context
)
796 def create(self
, cr
, uid
, vals
, context
=None):
797 member_line_obj
= self
.pool
.get('membership.membership_line')
798 print("DEV: [pos_membership] [pos_order_line] [create]: vals=%s context=%s" % (str(vals
), str(context
)))
799 res
= super(pos_order_line
, self
).create(cr
, uid
, vals
, context
=context
)
800 print ("DEV: [pos_membership] [pos_order_line] [create] [super] : res=%s" % str(res
))
801 line
= self
.browse(cr
, uid
, res
, context
=context
)
803 ml_ids
= member_line_obj
.search(cr
, uid
, [('pos_order_line', '=', line
.id)], context
=context
)
804 if line
.product_id
and line
.product_id
.membership
and not ml_ids
:
805 date_from
= line
.product_id
.membership_date_from
806 date_to
= line
.product_id
.membership_date_to
807 if line
.order_id
.date_order
> date_from
and line
.order_id
.date_order
< date_to
:
808 date_from
= line
.order_id
.date_order
809 if line
.product_id
.membership_date2date
:
810 date_from
= ('date_from' in context
811 and context
.get('date_from')
812 and datetime
.strptime(context
.get('date_from'), "%Y-%m-%d")
814 date_to
= date_from
+ relativedelta(days
= +364) # TODO: parameterize this delta?
815 date_from
= date_from
.strftime("%Y-%m-%d")
816 date_to
= date_to
.strftime("%Y-%m-%d")
817 print ("DEV: [pos_membership] [pos_order_line] [create] date_from: %s" % str(date_from
))
818 print ("DEV: [pos_membership] [pos_order_line] [create] date_to : %s" % str(date_to
))
819 member_line_obj
.create(cr
, uid
, {
820 'partner': line
.order_id
.partner_id
and line
.order_id
.partner_id
.id or False,
821 'membership_id': line
.product_id
.id,
822 'member_price': line
.price_unit
,
823 'date': time
.strftime('%Y-%m-%d'),
824 'date_from': date_from
,
826 'pos_order_line': line
.id,
828 partners
= [line
.order_id
.partner_id
]
829 if line
.product_id
.membership_grouped
and line
.order_id
.partner_id
.associate_members
:
830 partners
.extend(line
.order_id
.partner_id
.associate_members
)
831 #Adding membership lines just for associate partners
832 for associate_member
in line
.order_id
.partner_id
.associate_members
:
833 print ("DEV: [pos_membership] [pos_order_line] [create] [associate] date_from: %s" % str(date_from
))
834 print ("DEV: [pos_membership] [pos_order_line] [create] [associate] date_to : %s" % str(date_to
))
835 member_line_obj
.create(cr
, uid
, {
836 'partner': associate_member
.id,
837 'membership_id': line
.product_id
.id,
838 'member_price': line
.price_unit
,
839 'date': time
.strftime('%Y-%m-%d'),
840 'date_from': date_from
,
842 'pos_order_line': line
.id,
844 elif line
.product_id
.membership_grouped
and not line
.order_id
.partner_id
.associate_members
:
845 raise osv
.except_osv(_('Error!!!'), _('You try to order grouped membership product to a partner who hasn\'t associated partners.'))
846 #Define member ident if it's necessary
849 print ("\033[7mXXX: [pos_membership] [pos_order_line] [create] : pos_order_line has no partner_id!\033[0m")
851 if not i
.member_ident
:
852 print ("DEV: [pos_membership] [pos_order_line] [create] [partners] : i=%s i.id=%s" % (str(i
), str(i
.id)))
853 mbr_id
= self
.pool
.get('ir.sequence').get(cr
, uid
, 'member_ident')
854 self
.pool
.get('res.partner').write(cr
, uid
, i
.id, {'member_ident': mbr_id
})
855 print ("DEV: [pos_membership] [pos_order_line] [create] : return=%s" % str(res
))
860 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: