def notify_stock_limits(self): conf = Configuration.conf() try: sender = conf['default_sender'] except KeyError: raise ValueError('Default sender address not defined') locations = Location.objects.filter(site_id=settings.SITE_ID) for l in locations: out_of_stock = Inventory.objects.filter( location=l, amount_stocked__lt=F('amount_minimum') ) table = CsvTable() table.addheader(['Product', 'Minimum', 'Stocked']) for i in out_of_stock: table.addrow([i.product.code, i.amount_minimum, i.amount_stocked]) subject = _(u"Products stocked below limit") if Configuration.notify_location(): send_table(sender, l.email, subject, table) if Configuration.notify_email_address(): send_table(sender, conf['notify_address'], subject, table)
def test_checkin(self): uid = Configuration.conf('checkin_user') u = User.objects.get(pk=uid) o = Order(created_by=u) o.save() o.check_in(u) self.assertEquals(o.location, o.checkin_location)
def trigger_product_received(sender, instance, created, **kwargs): if instance.received_at is None: return product = instance.product po = instance.purchase_order location = po.created_by.get_location() inventory = Inventory.objects.get_or_create(location=location, product=product)[0] # Receiving an incoming item if Configuration.track_inventory(): try: inventory.amount_ordered -= instance.amount inventory.amount_stocked += instance.amount inventory.save() except Exception: ref = po.reference or po.confirmation ed = {'prod': product.code, 'ref': ref} raise ValueError(_('Cannot receive item %(prod)s (%(ref)s)') % ed) sales_order = instance.purchase_order.sales_order if sales_order is None: return # Trigger status change for parts receive if sales_order.queue: new_status = sales_order.queue.status_products_received if new_status and sales_order.is_editable: user = instance.received_by or instance.created_by sales_order.set_status(new_status, user)
def notify_aging_repairs(self): """ Reports on cases that have been red for a day """ now = timezone.now() limit = now - timedelta(days=1) for l in Location.objects.filter(enabled=True): table = CsvTable() subject = _(u"Repairs aging beyond limits at %s") % l.title table.addheader(['ORDER', 'ASSIGNED_TO', 'STATUS', 'DAYS_RED']) # "Aging" repairs are ones that have been red for at least a day orders = Order.objects.filter(location=l, state__lt=Order.STATE_CLOSED, status_limit_yellow__lt=limit) for o in orders: username = o.get_user_name() or _("Nobody") status_title = o.get_status_name() or _("No Status") days = (now - o.status_limit_yellow).days table.addrow([o.code, username, status_title, days]) if Configuration.notify_location(): recipient = l.manager.email if l.manager else l.email send_table(self.mail_sender, recipient, subject, table) if self.mail_recipient: send_table(self.mail_sender, self.mail_recipient, subject, table)
def send(self): """ Sends SMS through SMS Jazz Gateway """ conf = Configuration.conf() if not conf.get('sms_http_sender'): raise ValueError(_("SMS sender name not configured")) body = self.note.body.encode('utf-8') sender = conf.get('sms_http_sender') pwhash = md5(conf['sms_http_password']).hexdigest() checksum = md5(body + self.recipient.encode('ascii') + pwhash).hexdigest() params = { 'username' : conf['sms_http_user'], 'password' : pwhash, 'message' : body, 'sender' : sender.encode('ascii', 'replace'), 'receiver' : self.recipient, 'charset' : 'UTF8', 'checksum' : checksum, } if self.msg: dlruri = '/api/messages/?id={0}&status=%status%'.format(self.msg.code) dlruri = settings.SERVO_URL + dlruri params['dlruri'] = dlruri params = urllib.urlencode(params) r = urllib.urlopen(self.URL, params, context=_create_unverified_context()).read() if not '1:OK' in r: raise ValueError(_('Failed to send message to %s') % self.recipient)
def get_print_dict(self, kind='confirmation'): """ Return context dict for printing this order """ r = {'order': self} r['conf'] = Configuration.conf() r['title'] = _(u"Service Order #%s") % self.code r['notes'] = self.note_set.filter(is_reported=True) # TODO: replace with constants r['issues'] = r['notes'].filter(type=1) r['diagnoses'] = r['notes'].filter(type=3) r['tech_notes'] = r['notes'].filter(type=0) r['verified_issues'] = r['notes'].filter(type=4) r['customer_notes'] = r['notes'].filter(type=5) if kind == 'receipt': try: # Include the latest invoice data for receipts r['invoice'] = self.invoice_set.latest() except Exception as e: pass return r
def from_gsx(cls, code, gsx_account): """ Searches for a part from GSX and initializes Product with the data and our config """ conf = Configuration.conf() getcontext().rounding = ROUND_UP vat = Decimal(conf['pct_vat']) margin = Decimal(conf['pct_margin']) shipping = Decimal(conf['shipping_cost']) part = gsx.Part(partNumber=code).lookup() sp = Decimal(part.stockPrice) ep = Decimal(part.exchangePrice) sp = (sp+(sp/100*margin)).quantize(Decimal('1.')) + shipping ep = (ep+(ep/100*margin)+(ep/100*vat)).quantize(Decimal('1.')) + shipping product = Product(code=part.partNumber, title=part.partDescription, price_purchase=part.stockPrice, price_exchange=ep, pct_margin=margin, pct_vat=vat, price_notax=sp, warranty_period=3, brand='Apple', shipping=conf['shipping_cost'], component_code=part.componentCode, is_serialized=(part.isSerialized == 'Y'), price_sales=sp+(sp/100*vat).quantize(Decimal('1.')) ) return product
def send_mail(self, user): """ Sends this note as an email """ mailto = self.mailto() # Only send the same note once if self.has_sent_message(mailto): raise ValueError(_('Already sent message to %s') % mailto) config = Configuration.conf() smtp_host = config.get('smtp_host').split(':') settings.EMAIL_HOST = smtp_host[0] if len(smtp_host) > 1: settings.EMAIL_PORT = int(smtp_host[1]) settings.EMAIL_USE_TLS = config.get('smtp_ssl') settings.EMAIL_HOST_USER = str(config.get('smtp_user')) settings.EMAIL_HOST_PASSWORD = str(config.get('smtp_password')) headers = {} headers['Reply-To'] = self.sender headers['References'] = '%s.%s' % (self.code, self.sender) subject = u'%s (SRO#%s)' % (self.subject, self.code) if self.order: # Encode the SO code so that we can match replies to the SO # even if the original note has been deleted subject = u'%s (SRO#%s/%s)' % (self.subject, self.code, self.order.url_code) recipients = mailto.split(',') msg = EmailMessage(subject, self.body, self.sender, recipients, headers=headers) for f in self.attachments.all(): msg.attach_file(f.content.path) msg.send() for r in recipients: msg = Message(note=self, recipient=r, created_by=user, body=self.body) msg.sent_at = timezone.now() msg.sender = self.sender msg.status = 'SENT' msg.save() message = _(u'Message sent to %s') % mailto self.notify('email_sent', message, user) return message
def send_table(sender, recipient, subject, table, send_empty=False): if send_empty is False and table.has_body() is False: return try: validate_email(sender) validate_email(recipient) except Exception: return config = Configuration.conf() host, port = Configuration.get_smtp_server() settings.EMAIL_HOST = host settings.EMAIL_PORT = int(port) settings.EMAIL_USE_TLS = config.get('smtp_ssl') settings.EMAIL_HOST_USER = config.get('smtp_user') settings.EMAIL_HOST_PASSWORD = config.get('smtp_password') send_mail(subject, unicode(table), sender, [recipient], fail_silently=False)
def send_table(sender, recipient, subject, table, send_empty=False): from django.core.mail import send_mail if not send_empty and not table.has_body(): return config = Configuration.conf() settings.EMAIL_HOST = config.get('smtp_host') settings.EMAIL_USE_TLS = config.get('smtp_ssl') settings.EMAIL_HOST_USER = config.get('smtp_user') settings.EMAIL_HOST_PASSWORD = config.get('smtp_password') send_mail(subject, unicode(table), sender, [recipient], fail_silently=False)
def send_sms(self, number, user): """ Sends message as SMS """ number = validate_phone_number(number) if self.has_sent_message(number): raise ValueError(_('Already sent message to %s') % number) conf = Configuration.conf() sms_gw = conf.get('sms_gateway') if not sms_gw: raise ValueError(_("SMS gateway not configured")) msg = Message(note=self, recipient=number, created_by=user, body=self.body) if sms_gw == 'hqsms': from servo.messaging.sms import HQSMSProvider HQSMSProvider(number, self, msg).send() if sms_gw == 'jazz': from servo.messaging.sms import SMSJazzProvider SMSJazzProvider(number, self, msg).send() #self.send_sms_jazz(number, conf.get('sms_http_sender', ''), msg) if sms_gw == 'http': from servo.messaging.sms import HttpProvider HttpProvider(self, number).send() if sms_gw == 'smtp': gw_address = conf.get('sms_smtp_address') if not gw_address: raise ValueError('Missing SMTP SMS gateway address') self.send_sms_smtp(conf, number) if sms_gw == 'builtin': self.send_sms_builtin(number) msg.method = 'SMS' msg.status = 'SENT' msg.sent_at = timezone.now() msg.save() message = _('Message sent to %s') % number self.notify('sms_sent', message, self.created_by) return message
def close(self, user): self.notify("close_order", _(u"Order %s closed") % self.code, user) self.closed_by = user self.closed_at = timezone.now() self.state = self.STATE_CLOSED self.save() if Configuration.autocomplete_repairs(): for r in self.repair_set.filter(completed_at=None): r.close(user) if self.queue and self.queue.status_closed: self.set_status(self.queue.status_closed, user)
def notify_aging_repairs(self): """ Reports on cases that have been red for a day """ conf = Configuration.conf() try: sender = conf['default_sender'] except KeyError: raise ValueError('Default sender address not defined') now = timezone.now() limit = now - timedelta(days=1) locations = Location.objects.filter(site_id=settings.SITE_ID) for l in locations: table = CsvTable() table.addheader(['Order', 'Assigned To', 'Status', 'Days red']) # "Aging" repairs are ones that have been red for at least a day orders = Order.objects.filter( location=l, state__lt=Order.STATE_CLOSED, status_limit_yellow__lt=limit ) for o in orders: username = o.get_user_name() or _("Unassigned") status_title = o.get_status_name() or _("No Status") days = (now - o.status_limit_yellow).days table.addrow([o.code, username, status_title, days]) subject = _(u"Repairs aging beyond limits at %s") % l.title if Configuration.notify_location(): send_table(sender, l.email, subject, table) if Configuration.notify_email_address(): send_table(sender, conf['notify_address'], subject, table)
def send_sms(self): import urllib conf = Configuration.conf() params = urllib.urlencode({ 'username': conf['sms_user'], 'password': conf['sms_password'], 'text': self.body, 'to': self.smsto }) f = urllib.urlopen('%s?%s' %(conf.sms_url, params)) self.sent_at = datetime.now() self.save() print f.read()
def send(self, note, number): conf = Configuration.conf() if not conf.get('sms_http_url'): raise ValueError(_("No SMS HTTP gateway defined")) params = urllib.urlencode({ 'username' : conf['sms_http_user'], 'password' : conf['sms_http_password'], 'text' : note.body.encode('utf8'), 'to' : number }) f = urllib.urlopen("%s?%s" % (conf['sms_http_url'], params), context=_create_unverified_context()) return f.read()
def __init__(self, recipient, note, msg): self.conf = Configuration.conf() if not self.conf.get('sms_http_sender'): raise ValueError(_("SMS sender not configured")) if len(recipient) < 8: recipient = '372' + recipient recipient = re.sub(r'[\+\s\-]', '', recipient) self.recipient = recipient.lstrip('+') self.note = note self.msg = msg self.sender = self.conf['sms_http_sender'] self.body = self.note.body.encode('utf-8')
def update_invoices(self): uid = Configuration.conf('imap_act') user = User.objects.get(pk=uid) for a in GsxAccount.objects.all(): try: a.default(user) lookup = gsxws.Lookup(shipTo=a.ship_to, invoiceDate=date.today()) invoices = lookup.invoices() for i in invoices: details = gsxws.Lookup(invoiceID=i).invoice_details() # @TODO: What about stock orders? po = PurchaseOrder.objects.get(pk=details.purchaseOrderNumber) po.invoice_id = i po.invoice.save("%s.pdf" % i, File(open(details.invoiceData))) except Exception, e: raise e
def get_print_dict(self, kind='confirmation'): """ Return context dict for printing this order """ r = {} r['order'] = self r['conf'] = Configuration.conf() r['title'] = _(u"Service Order #%s") % self.code r['notes'] = self.note_set.filter(is_reported=True) if kind == 'receipt': try: # Include the latest invoice data for receipts r['invoice'] = self.invoice_set.latest() except Exception as e: pass return r
def put_on_paper(request, pk, kind="confirmation"): """ 'Print' was taken? """ conf = Configuration.conf() order = get_object_or_404(Order, pk=pk) title = _(u"Service Order #%s") % order.code notes = order.note_set.filter(is_reported=True) template = order.get_print_template(kind) if kind == "receipt": try: invoice = order.invoice_set.latest() except Exception as e: pass return render(request, template, locals())
def get_print_name(self): """ Returns name of user/tech according to system settings """ conf = Configuration.conf('checkin_tech_name') if conf == 'full': return self.get_full_name() if conf == 'short': return self.get_shortname() if conf == 'first': return self.first_name if conf == 'last': return self.last_name if conf == 'none': return ''
def close(self, user): """Close this service order.""" if Configuration.autocomplete_repairs(): for r in self.repair_set.active(): try: r.set_status_code("RFPU") r.close(user) except Exception as e: # notify the creator of the GSX repair instead of just erroring out e = self.notify("gsx_error", e, user) e.notify_users.add(r.created_by) if self.queue and self.queue.status_closed: self.set_status(self.queue.status_closed, user) self.notify("close_order", _(u"Order %s closed") % self.code, user) self.closed_by = user self.closed_at = timezone.now() self.state = self.STATE_CLOSED self.save()
def close(self, user): """Close this service order.""" if Configuration.autocomplete_repairs(): for r in self.repair_set.active(): try: r.set_status_code('RFPU') r.close(user) except Exception as e: # notify the creator of the GSX repair instead of just erroring out e = self.notify("gsx_error", e, user) e.notify_users.add(r.created_by) if self.queue and self.queue.status_closed: self.set_status(self.queue.status_closed, user) self.notify("close_order", _(u"Order %s closed") % self.code, user) self.closed_by = user self.closed_at = timezone.now() self.state = self.STATE_CLOSED self.save()
def notify_stock_limits(self): """ Notifies the correct parties of inventory items stocking status """ subject = _(u"Products stocked below limit") for l in Location.objects.filter(enabled=True): out_of_stock = Inventory.objects.filter( location=l, amount_stocked__lt=F('amount_minimum') ) table = CsvTable() table.addheader(['PRODUCT', 'MINIMUM', 'STOCKED']) for i in out_of_stock: table.addrow([i.product.code, i.amount_minimum, i.amount_stocked]) if Configuration.notify_location(): recipient = l.manager.email if l.manager else l.email send_table(self.mail_sender, recipient, subject, table) if self.mail_recipient: send_table(self.mail_sender, self.mail_recipient, subject, table)
def send_mail(self): import smtplib from email import encoders from email.mime.base import MIMEBase from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart conf = Configuration.conf() server = smtplib.SMTP(conf['smtp_host']) if conf['smtp_user']: server.login(conf['smtp_user'], conf['smtp_password']) subject = 'Huoltoviesti SRV#%s' %(self.code) msg = MIMEMultipart() msg['To'] = self.mailto() msg['Subject'] = subject msg['From'] = conf['mail_from'] msg['In-Reply-To'] = str(self.code) txt = MIMEText(self.body, 'plain', 'utf-8') msg.attach(txt) for f in self.attachments.all(): maintype, subtype = f.content_type.split('/', 1) a = MIMEBase(maintype, subtype) a.set_payload(f.content.read()) encoders.encode_base64(a) msg.add_header('Content-Disposition', 'attachment', filename=f.name) msg.attach(a) self.sent_at = datetime.now() self.save() server.sendmail(msg['From'], msg['To'], msg.as_string()) server.quit()
def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) uid = Configuration.conf('imap_act') self.mail_user = User.objects.get(pk=uid) self.mail_recipient = Configuration.notify_email_address() self.mail_sender = Configuration.get_default_sender(self.mail_user)
def default_vat(): conf = Configuration.conf() return conf.get('pct_vat', 0.0)
def get_checkin_group(cls): """ Returns all the active members of the check-in group """ group = Configuration.conf('checkin_group') return cls.active.filter(groups__pk=group)
def currency(value): try: c = Configuration.conf('currency') return babel.numbers.format_currency(decimal.Decimal(value), c) except Exception: return value
def get_default_sender(self): return Configuration.get_default_sender(self.created_by)
def send_mail(self, user): """Sends this note as an email""" mailto = self.mailto() # Only send the same note once if self.has_sent_message(mailto): raise ValueError(_('Already sent message to %s') % mailto) config = Configuration.conf() smtp_host = config.get('smtp_host').split(':') settings.EMAIL_HOST = smtp_host[0] if len(smtp_host) > 1: settings.EMAIL_PORT = int(smtp_host[1]) if config.get('SMTP_ENCRYPTION') == 'TLS': settings.EMAIL_USE_TLS = True if config.get('SMTP_ENCRYPTION') == 'SSL': settings.EMAIL_USE_SSL = True if config.get('SMTP_ENCRYPTION') == 'OFF': settings.EMAIL_USE_SSL = False settings.EMAIL_USE_TLS = False settings.EMAIL_HOST_USER = str(config.get('smtp_user')) settings.EMAIL_HOST_PASSWORD = str(config.get('smtp_password')) headers = {} headers['Reply-To'] = self.sender headers['References'] = '%s.%s' % (self.code, self.sender) subject = u'%s (SRO#%s)' % (self.subject, self.code) if self.order: # Encode the SO code so that we can match replies to the SO # even if the original note has been deleted subject = u'%s (SRO#%s/%s)' % (self.subject, self.code, self.order.url_code) recipients = mailto.split(',') msg = EmailMessage(subject, self.body, self.sender, recipients, headers=headers) for f in self.attachments.all(): msg.attach_file(f.content.path) msg.send() for r in recipients: msg = Message(note=self, recipient=r, created_by=user, body=self.body) msg.sent_at = timezone.now() msg.sender = self.sender msg.status = 'SENT' msg.save() message = _(u'Message sent to %s') % mailto self.notify('email_sent', message, user) return message
def get_checkin_user(cls): return cls.objects.get(pk=Configuration.conf('checkin_user'))
def test_checkin_user(self): uid = Configuration.conf('checkin_user') u = User.objects.get(pk=uid) self.assertEquals(u.pk, int(uid))
def check_in(self, user): """ Checks this Order in through the check-in process """ queue_id = Configuration.conf('checkin_queue') self.set_queue(queue_id, user)
def default_margin(): conf = Configuration.conf() return conf.get('pct_margin', 0.0)
def check_in(self, user): """Check this Order in through the check-in process.""" queue_id = Configuration.conf('checkin_queue') self.set_queue(queue_id, user)