aboutsummaryrefslogtreecommitdiffstats
path: root/servo/management
diff options
context:
space:
mode:
Diffstat (limited to 'servo/management')
-rw-r--r--servo/management/__init__.py0
-rw-r--r--servo/management/commands/__init__.py0
-rw-r--r--servo/management/commands/backup.py155
-rw-r--r--servo/management/commands/checkescalations.py71
-rw-r--r--servo/management/commands/checkmail.py58
-rw-r--r--servo/management/commands/cleandups.py42
-rw-r--r--servo/management/commands/cleanphones.py43
-rw-r--r--servo/management/commands/cleanup.py56
-rw-r--r--servo/management/commands/clearcache.py36
-rwxr-xr-xservo/management/commands/cron.py164
-rw-r--r--servo/management/commands/dbbackup.py43
-rw-r--r--servo/management/commands/dbdump.py37
-rw-r--r--servo/management/commands/dbrestore.py42
-rw-r--r--servo/management/commands/deleteorders.py54
-rw-r--r--servo/management/commands/fixfollowers.py43
-rw-r--r--servo/management/commands/fixproducts.py43
-rw-r--r--servo/management/commands/fixtimestamps.py46
-rw-r--r--servo/management/commands/importparts.py127
-rw-r--r--servo/management/commands/migratepayments.py44
-rw-r--r--servo/management/commands/migratestatuses.py54
-rw-r--r--servo/management/commands/migratetimezones.py41
-rw-r--r--servo/management/commands/obfuscate.py107
-rw-r--r--servo/management/commands/slugifycategories.py38
-rw-r--r--servo/management/commands/sutostaff.py43
-rw-r--r--servo/management/commands/tokenize.py39
-rw-r--r--servo/management/commands/updatecomponents.py44
-rw-r--r--servo/management/commands/updateorders.py47
-rw-r--r--servo/management/commands/updatepototals.py38
-rw-r--r--servo/management/commands/updateprices.py63
-rw-r--r--servo/management/commands/updaterepairs.py45
30 files changed, 1663 insertions, 0 deletions
diff --git a/servo/management/__init__.py b/servo/management/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/servo/management/__init__.py
diff --git a/servo/management/commands/__init__.py b/servo/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/servo/management/commands/__init__.py
diff --git a/servo/management/commands/backup.py b/servo/management/commands/backup.py
new file mode 100644
index 0000000..90c2bd0
--- /dev/null
+++ b/servo/management/commands/backup.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import os
+import csv
+import shutil
+import subprocess
+from datetime import date
+from django.db import connection
+
+from django.core.management.base import BaseCommand
+
+
+def write(path, header, cursor):
+ with open(path, 'w') as fout:
+ writer = csv.writer(fout)
+ writer.writerow(header)
+
+ for row in cursor.fetchall():
+ row = [unicode(s).encode('utf-8') for s in row]
+ writer.writerow(row)
+
+
+class Command(BaseCommand):
+
+ help = 'Export this servo database in a portable format'
+
+ def handle(self, *args, **options):
+ backupdir = 'backups/%s' % date.today().isoformat()
+ os.mkdir(backupdir)
+
+ cursor = connection.cursor()
+
+ path = os.path.join(backupdir, 'notes.csv')
+ cursor.execute("""SELECT id, order_id, created_by_id, created_at, body
+ FROM servo_note""")
+ header = ['ID', 'ORDER_ID', 'USER_ID', 'CREATED_AT', 'NOTE']
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'users.csv')
+ header = ['ID', 'USERNAME', 'FIRST_NAME', 'LAST_NAME', 'EMAIL']
+ cursor.execute("""SELECT id, username, first_name, last_name, email
+ FROM servo_user WHERE is_visible = TRUE""")
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'orders.csv')
+ header = ['ID', 'CODE', 'CREATED_AT',
+ 'CLOSED_AT', 'CUSTOMER_ID', 'USER_ID', 'QUEUE_ID']
+ cursor.execute("""SELECT id, code, created_at, closed_at,
+ customer_id, user_id, queue_id
+ FROM servo_order""")
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'queues.csv')
+ header = ['ID', 'NAME', 'DESCRIPTION',
+ 'CLOSED_AT', 'CUSTOMER_ID', 'USER_ID', 'QUEUE_ID']
+ cursor.execute("""SELECT id, title, description FROM servo_queue""")
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'devices.csv')
+ header = ['ID', 'SERIAL_NUMBER', 'IMEI',
+ 'CONFIGURATION', 'WARRANTY_STATUS', 'PURCHASE_DATE', 'NOTES']
+ cursor.execute("""SELECT id, sn, imei, configuration, warranty_status,
+ purchased_on, notes FROM servo_device""")
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'repairs.csv')
+ header = ['ID', 'ORDER_ID', 'DEVICE_ID', 'USER_ID',
+ 'SUBMITTED_AT', 'COMPLETED_AT', 'REQUEST_REVIEW',
+ 'TECH_ID', 'UNIT_RECEIVED', 'CONFIRMATION',
+ 'REFERENCE', 'SYMPTOM', 'DIAGNOSIS', 'NOTES']
+ cursor.execute("""SELECT id, order_id, device_id,
+ created_by_id, submitted_at, completed_at,
+ request_review, tech_id, unit_received_at, confirmation, reference,
+ symptom, diagnosis, notes
+ FROM servo_repair
+ WHERE submitted_at IS NOT NULL""")
+ write(path, header, cursor)
+
+ header = ['ID', 'CODE', 'TITLE', 'DESCRIPTION',
+ 'PRICE_PURCHASE_EXCHANGE', 'PRICE_PURCHASE_STOCK',
+ 'PRICE_SALES_EXCHANGE', 'PRICE_SALES_STOCK', 'COMPONENT_CODE',
+ 'PART_TYPE', 'EEE_CODE']
+ cursor.execute("""SELECT id, code, title, description,
+ price_purchase_exchange, price_purchase_stock,
+ price_sales_exchange, price_sales_stock,
+ component_code, part_type, eee_code
+ FROM servo_product""")
+ path = os.path.join(backupdir, 'products.csv')
+ write(path, header, cursor)
+
+ header = ['ID', 'PARENT_ID', 'NAME', 'PHONE', 'EMAIL',
+ 'STREET_ADDRESS', 'POSTAL_CODE', 'CITY'
+ 'COUNTRY', 'NOTES']
+ cursor.execute("""SELECT id, parent_id, name, phone,
+ email, street_address, zip_code, city, country, notes
+ FROM servo_customer""")
+ path = os.path.join(backupdir, 'customers.csv')
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'order_products.csv')
+ header = ['ID', 'PRODUCT_ID', 'ORDER_ID', 'CODE', 'TITLE',
+ 'DESCRIPTION', 'AMOUNT', 'SERIAL_NUMBER', 'KBB_SN',
+ 'IMEI', 'REPORTED', 'PRICE_CATEGORY', 'PRICE'
+ 'COMPTIA_CODE', 'COMPTIA_MODIFIER']
+ cursor.execute("""SELECT id, product_id, order_id, code,
+ title, description, amount, sn, price, kbb_sn,
+ imei, should_report, price_category, price,
+ comptia_code, comptia_modifier
+ FROM servo_serviceorderitem""")
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'parts.csv')
+ header = ['ID', 'REPAIR_ID', 'ORDER_ITEM_ID',
+ 'NUMBER', 'TITLE', 'COMPTIA_CODE', 'COMPTIA_MODIFIER',
+ 'RETURN_ORDER', 'RETURN_STATUS', 'RETURN_CODE',
+ 'ORDER_STATUS', 'COVERAGE', 'SHIP_TO', 'RETURNED_AT']
+ cursor.execute("""SELECT id, repair_id, order_item_id,
+ part_number, part_title, comptia_code, comptia_modifier,
+ return_order, return_status, return_code,
+ order_status, coverage_description, ship_to, returned_at
+ FROM servo_servicepart""")
+ write(path, header, cursor)
+
+ path = os.path.join(backupdir, 'order_devices.csv')
+ header = ['ID', 'ORDER_ID', 'DEVICE_ID', 'REPORTED']
+ cursor.execute("""SELECT id, order_id, device_id, should_report
+ FROM servo_orderdevice""")
+ write(path, header, cursor)
+
+ subprocess.call(['tar', '-C', backupdir, '-zcf', '%s.tar.gz' % backupdir, '.'])
+ shutil.rmtree(backupdir, ignore_errors=True)
diff --git a/servo/management/commands/checkescalations.py b/servo/management/commands/checkescalations.py
new file mode 100644
index 0000000..7a49244
--- /dev/null
+++ b/servo/management/commands/checkescalations.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.db.models import Q
+from django.utils import timezone
+from django.core.management.base import BaseCommand, CommandError
+
+from servo.models import Configuration, Note, User, Escalation
+
+
+class Command(BaseCommand):
+ help = "Check updates for open escalations"
+
+ def handle(self, *args, **options):
+ uid = Configuration.conf('imap_act')
+
+ if uid in [None, '']:
+ return
+
+ user = User.objects.get(pk=uid)
+ tz = timezone.get_current_timezone()
+
+ for i in Escalation.objects.exclude(Q(escalation_id='') | Q(status='C')):
+ i.gsx_account.connect(i.created_by)
+ r = i.get_escalation().lookup()
+ aware = timezone.make_aware(r.lastModifiedTimestamp, tz)
+
+ if aware < i.updated_at: # hasn't been updated
+ continue
+
+ try:
+ parent = i.note_set.latest()
+ except Note.DoesNotExist:
+ continue
+
+ bodies = [n.body for n in i.note_set.all()]
+
+ for x in r.escalationNotes.iterchildren():
+ if x.text in bodies: # skip notes we already have
+ continue
+
+ note = Note(created_by=user, escalation=i, body=x.text)
+ parent.add_reply(note)
+ note.save()
+
+ i.updated_at = timezone.now()
+ i.status = r.escalationStatus
+ i.save()
diff --git a/servo/management/commands/checkmail.py b/servo/management/commands/checkmail.py
new file mode 100644
index 0000000..891d6aa
--- /dev/null
+++ b/servo/management/commands/checkmail.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import logging
+from email.parser import Parser
+
+from django.core.management.base import BaseCommand
+
+from servo.models import Configuration, Note, User
+
+
+class Command(BaseCommand):
+ help = "Checks IMAP box for new mail"
+
+ def handle(self, *args, **options):
+ uid = Configuration.conf('imap_act')
+
+ if uid == '':
+ raise ValueError('Incoming message user not configured')
+
+ user = User.objects.get(pk=uid)
+ server = Configuration.get_imap_server()
+ typ, data = server.search(None, "UnSeen")
+
+ for num in data[0].split():
+ #logging.debug("** Processing message %s" % num)
+ typ, data = server.fetch(num, "(RFC822)")
+ # parsestr() seems to return an email.message?
+ msg = Parser().parsestr(data[0][1])
+ Note.from_email(msg, user)
+ #server.copy(num, 'servo')
+ server.store(num, '+FLAGS', '\\Seen')
+
+ server.close()
+ server.logout()
diff --git a/servo/management/commands/cleandups.py b/servo/management/commands/cleandups.py
new file mode 100644
index 0000000..fa333bf
--- /dev/null
+++ b/servo/management/commands/cleandups.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import logging
+from django.core.management.base import BaseCommand
+from django.template.defaultfilters import slugify
+
+from servo.models import TaggedItem
+
+
+class Command(BaseCommand):
+
+ help = "Cleans various duplicate data"
+
+ def handle(self, *args, **options):
+ logging.info("Cleaning up duplicate tags")
+ for d in TaggedItem.objects.filter(slug=None):
+ d.slug = slugify(d.description)
+ d.save()
diff --git a/servo/management/commands/cleanphones.py b/servo/management/commands/cleanphones.py
new file mode 100644
index 0000000..4bc2371
--- /dev/null
+++ b/servo/management/commands/cleanphones.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+
+from servo.models import Customer
+
+
+class Command(BaseCommand):
+ help = "Cleans illegal characters from phone numbers"
+
+ def handle(self, *args, **options):
+ ALLOWED_CHARS = r'^[\d\s\+\-]+$'
+ for i in Customer.objects.exclude(phone__regex=ALLOWED_CHARS):
+ if i.phone == '':
+ continue
+
+ i.notes = i.notes + i.phone
+ i.phone = ''
+ i.save()
diff --git a/servo/management/commands/cleanup.py b/servo/management/commands/cleanup.py
new file mode 100644
index 0000000..8f6f39e
--- /dev/null
+++ b/servo/management/commands/cleanup.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import os
+import Image
+import logging
+from glob import glob
+from django.core.management.base import BaseCommand
+
+from servo.models import Attachment
+
+
+class Command(BaseCommand):
+
+ help = "Does various cleanup"
+
+ def handle(self, *args, **options):
+ size = 128, 128
+ logging.info("Building avatar thumbnails")
+ for infile in glob("servo/uploads/avatars/*.jpg"):
+ logging.info(infile)
+ im = Image.open(infile)
+ im.thumbnail(size, Image.ANTIALIAS)
+ im.save(infile, "JPEG")
+
+ logging.info("Cleaning up unused attachments")
+ for infile in glob("servo/uploads/attachments/*"):
+ fn = infile.decode('utf-8')
+ fp = os.path.join("attachments", os.path.basename(fn))
+ try:
+ Attachment.objects.get(content=fp)
+ except Attachment.DoesNotExist:
+ os.remove(infile)
diff --git a/servo/management/commands/clearcache.py b/servo/management/commands/clearcache.py
new file mode 100644
index 0000000..826ddef
--- /dev/null
+++ b/servo/management/commands/clearcache.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.cache import cache
+from django.core.management.base import BaseCommand
+
+
+class Command(BaseCommand):
+
+ help = "Clears this install's cache"
+
+ def handle(self, *args, **options):
+ cache.clear()
diff --git a/servo/management/commands/cron.py b/servo/management/commands/cron.py
new file mode 100755
index 0000000..e2831e9
--- /dev/null
+++ b/servo/management/commands/cron.py
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import gsxws
+from datetime import date, timedelta
+
+from django.conf import settings
+from django.core.files import File
+from django.utils.translation import ugettext as _
+
+from django.core.management.base import BaseCommand
+
+from django.db.models import F
+from django.utils import timezone
+from django.core.cache import cache
+
+from servo.models.common import Configuration, Location, CsvTable, GsxAccount
+from servo.models import Inventory, Order, PurchaseOrder, User
+
+
+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)
+
+
+class Command(BaseCommand):
+ help = "Runs Servo's periodic commands"
+
+ 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 update_warranty(self):
+ pass
+
+ 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 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 update_counts(self):
+ now = timezone.now()
+ orders = Order.objects.filter(state__lt=Order.STATE_CLOSED)
+ green = orders.filter(status_limit_green__gte=now)
+ cache.set('green_order_count', green.count())
+ yellow = orders.filter(status_limit_yellow__gte=now)
+ cache.set('yellow_order_count', yellow.count())
+ red = orders.filter(status_limit_yellow__lte=now)
+ cache.set('red_order_count', red.count())
+
+ def handle(self, *args, **options):
+ #self.update_invoices()
+ self.update_counts()
+ self.notify_aging_repairs()
+ self.notify_stock_limits()
diff --git a/servo/management/commands/dbbackup.py b/servo/management/commands/dbbackup.py
new file mode 100644
index 0000000..91d3875
--- /dev/null
+++ b/servo/management/commands/dbbackup.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import sys
+import logging
+import subprocess
+from django.conf import settings
+from django.core.management.base import BaseCommand
+
+
+class Command(BaseCommand):
+ help = 'Backup this Servo database'
+ def handle(self, *args, **options):
+ if len(args) < 1:
+ print 'Usage: dbbackup file'
+ sys.exit(1)
+ db = settings.DATABASES['default']
+ pg_dump = subprocess.check_output(['which', 'pg_dump']).strip()
+ subprocess.call([pg_dump, '-Fc', db['NAME'], '-U', db['USER'],
+ '-f' , args[0]], env={'PGPASSWORD': db['PASSWORD']})
diff --git a/servo/management/commands/dbdump.py b/servo/management/commands/dbdump.py
new file mode 100644
index 0000000..12023c4
--- /dev/null
+++ b/servo/management/commands/dbdump.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import subprocess
+from django.conf import settings
+from django.core.management.base import BaseCommand
+
+
+class Command(BaseCommand):
+ help = "Dumps DB of this instance to specified file"
+
+ def handle(self, *args, **options):
+ dbname = settings.DATABASES['default']['NAME']
+ #subprocess.call('pg_dump', '-Fc', dbname, '-U', 'pgsql' > "${BACKUPDIR}/${db}_$(date "+%Y%m%d_%H%M").pgdump"
diff --git a/servo/management/commands/dbrestore.py b/servo/management/commands/dbrestore.py
new file mode 100644
index 0000000..640d3d6
--- /dev/null
+++ b/servo/management/commands/dbrestore.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import subprocess
+from django.conf import settings
+from django.core.management.base import BaseCommand
+
+
+class Command(BaseCommand):
+ help = 'Restore this Servo database from backup'
+ def handle(self, *args, **options):
+ if len(args) < 1:
+ print 'Usage: dbrestore file'
+ sys.exit(1)
+
+ db = settings.DATABASES['default']
+ pg_restore = subprocess.check_output(['which', 'pg_restore']).strip()
+ subprocess.call([pg_restore, '-d', db['NAME'], '-O', '-x', '-U', db['USER'],
+ args[0]], env={'PGPASSWORD': db['PASSWORD']})
diff --git a/servo/management/commands/deleteorders.py b/servo/management/commands/deleteorders.py
new file mode 100644
index 0000000..e531029
--- /dev/null
+++ b/servo/management/commands/deleteorders.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import logging
+from django.core.management.base import BaseCommand
+
+from servo.models import (Order, Repair,)
+
+class Command(BaseCommand):
+ help = "Deletes orders listed in text file"
+
+ def handle(self, *args, **options):
+ if len(args) < 1:
+ print 'Usage: deleteorders data.txt'
+ sys.exit(1)
+
+ counter = 0
+ dataf = open(args[0], 'r')
+
+ for l in dataf.readlines():
+ cols = l.strip().split("\t")
+ try:
+ print 'Deleting order %s' % cols[0]
+ order = Order.objects.get(code=cols[0])
+ Repair.objects.filter(order=order).delete()
+ order.delete()
+ counter += 1
+ except Order.DoesNotExist:
+ pass
+
+ print '%d orders deleted' % counter
diff --git a/servo/management/commands/fixfollowers.py b/servo/management/commands/fixfollowers.py
new file mode 100644
index 0000000..bd8e1cb
--- /dev/null
+++ b/servo/management/commands/fixfollowers.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+
+from servo.models import Event
+
+
+class Command(BaseCommand):
+ help = "Fixes missing follower data from event logs"
+
+ def handle(self, *args, **options):
+ for i in Event.objects.filter(action='set_user'):
+ user = i.triggered_by
+ order = i.content_object
+ if order.user is None and 'unassigned' not in i.description:
+ print('Assigning %s to %s' % (order, user))
+ order.add_follower(user)
+ order.user = i.triggered_by
+ order.save()
diff --git a/servo/management/commands/fixproducts.py b/servo/management/commands/fixproducts.py
new file mode 100644
index 0000000..9767ab4
--- /dev/null
+++ b/servo/management/commands/fixproducts.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+from django.template.defaultfilters import slugify
+
+from servo.models import ServiceOrderItem, Product
+
+
+class Command(BaseCommand):
+ help = "Fixes SOI and Product codes that include spaces"
+
+ def handle(self, *args, **options):
+ for i in Product.objects.filter(code__contains=' '):
+ i.code = slugify(i.code)
+ i.save()
+
+ for i in ServiceOrderItem.objects.filter(code__contains=' '):
+ i.code = slugify(i.code)
+ i.save()
diff --git a/servo/management/commands/fixtimestamps.py b/servo/management/commands/fixtimestamps.py
new file mode 100644
index 0000000..81e1cdb
--- /dev/null
+++ b/servo/management/commands/fixtimestamps.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+from django.template.defaultfilters import slugify
+from django.contrib.contenttypes.models import ContentType
+
+from servo.models import OrderStatus
+
+
+class Command(BaseCommand):
+ help = "Fixes finished_at timestamps of OrderStatuses"
+
+ def handle(self, *args, **options):
+ for s in OrderStatus.objects.all():
+ next_status = s.get_next()
+
+ if next_status is None:
+ continue # current status
+
+ s.finished_at = next_status.started_at
+ s.duration = (s.finished_at - s.started_at).total_seconds()
+ s.save()
diff --git a/servo/management/commands/importparts.py b/servo/management/commands/importparts.py
new file mode 100644
index 0000000..8df1ecb
--- /dev/null
+++ b/servo/management/commands/importparts.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import os
+import logging
+
+from decimal import Decimal, InvalidOperation, ROUND_CEILING
+
+from django.db import DatabaseError
+from django.core.management.base import BaseCommand
+from django.contrib.contenttypes.models import ContentType
+
+from servo.models import Product, TaggedItem
+
+
+class Command(BaseCommand):
+
+ help = "Imports complete GSX parts database"
+
+ def handle(self, *args, **options):
+
+ update_prices = True
+ import_vintage = True
+ dbpath = "servo/uploads/products/partsdb.csv"
+
+ try:
+ partsdb = open(dbpath, "r")
+ except Exception:
+ pass
+
+ content_type = ContentType.objects.get(model="product")
+
+ for l in partsdb.readlines():
+
+ line = l.decode("iso-8859-1")
+ row = line.strip().split("\t")
+
+ if row[5] == "" or row[5] == "Currency":
+ continue # Skip header row and rows without currency
+
+ logging.debug(row)
+
+ category = row[0]
+
+ if re.match(r'~VIN', category) and not import_vintage:
+ continue # Skip vintage devices if so desired
+
+ p_number = row[1]
+
+ if re.match(r'675-', p_number):
+ continue # Skip DEPOT REPAIR INVOICE
+
+ p_title = row[2]
+ p_type = row[3]
+ lab_tier = row[4]
+
+ try:
+ stock_price = Decimal(row[6])
+ except InvalidOperation:
+ continue # Skip parts with no stock price
+
+ exchange_price = Decimal(row[7])
+
+ eee_code = row[8]
+
+ # skip substitute
+ component_group = row[10] or None
+ is_serialized = row[11]
+ req_diag = (row[12] == "Y")
+
+ product, created = Product.objects.get_or_create(code=p_number)
+
+ product.title = p_title
+ product.eee_code = eee_code
+ product.labour_tier = lab_tier
+ product.part_type = p_type or "OTHER"
+
+ product.component_code = component_group
+ product.is_serialized = (is_serialized == "Y")
+
+ if update_prices:
+ if stock_price:
+ purchase_sp = Decimal(stock_price)
+ product.price_purchase_stock = purchase_sp.to_integral_exact(rounding=ROUND_CEILING)
+ product.set_stock_sales_price()
+
+ if exchange_price:
+ purchase_ep = Decimal(exchange_price)
+ product.price_purchase_exchange = purchase_ep.to_integral_exact(rounding=ROUND_CEILING)
+ product.set_exchange_sales_price()
+
+ product.save()
+
+ try:
+ tag, created = TaggedItem.objects.get_or_create(
+ content_type=content_type,
+ object_id=product.pk,
+ tag=category)
+ tag.save()
+ except DatabaseError:
+ pass
+
+ os.unlink(dbpath)
diff --git a/servo/management/commands/migratepayments.py b/servo/management/commands/migratepayments.py
new file mode 100644
index 0000000..84c5c40
--- /dev/null
+++ b/servo/management/commands/migratepayments.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import logging
+
+from django.core.management.base import BaseCommand, CommandError
+
+from servo.models import Invoice, Payment
+
+
+class Command(BaseCommand):
+ help = "Migrate invoice payment info to Payments"
+
+ def handle(self, *args, **options):
+ Payment.objects.all().delete()
+ for i in Invoice.objects.all():
+ p = Payment(invoice=i, method=i.payment_method)
+ p.created_at = i.paid_at
+ p.created_by = i.created_by
+ p.amount = i.total_gross
+ p.save()
diff --git a/servo/management/commands/migratestatuses.py b/servo/management/commands/migratestatuses.py
new file mode 100644
index 0000000..e6ca601
--- /dev/null
+++ b/servo/management/commands/migratestatuses.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import logging
+
+from django.core.management.base import BaseCommand, CommandError
+
+from servo.models import Event, OrderStatus, Status
+
+
+class Command(BaseCommand):
+ help = "Migrate Events to Order Statuses"
+
+ def handle(self, *args, **options):
+ OrderStatus.objects.all().delete()
+
+ for i in Event.objects.filter(action='set_status'):
+ if i.content_object is None:
+ continue
+
+ os = OrderStatus(order=i.content_object)
+ os.started_by = i.triggered_by
+ os.started_at = i.triggered_at
+ os.finished_at = i.handled_at
+
+ try:
+ os.status = Status.objects.get(title=i.description)
+ except Status.DoesNotExist:
+ continue
+
+ os.save()
diff --git a/servo/management/commands/migratetimezones.py b/servo/management/commands/migratetimezones.py
new file mode 100644
index 0000000..7eb63d1
--- /dev/null
+++ b/servo/management/commands/migratetimezones.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import logging
+
+from django.core.management.base import BaseCommand, CommandError
+
+from servo.models import User
+
+
+class Command(BaseCommand):
+ help = "Migrate location timezones to user timezones"
+
+ def handle(self, *args, **options):
+ for i in User.objects.all():
+ if i.location:
+ i.timezone = i.location.timezone
+ i.save()
diff --git a/servo/management/commands/obfuscate.py b/servo/management/commands/obfuscate.py
new file mode 100644
index 0000000..8673846
--- /dev/null
+++ b/servo/management/commands/obfuscate.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from random import choice
+from django.db.utils import IntegrityError
+from django.template.defaultfilters import slugify
+from django.core.management.base import BaseCommand, CommandError
+
+from servo.models import Customer, Order, User, Location, GsxAccount
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options):
+ help = "Obfuscates the information in this Servo install"
+ names = ('Daniel Scott', 'Amy Collins', 'Linda Moore',
+ 'Dennis Parker', 'Mark Cox', 'Jesse Clark',
+ 'Brian Patterson', 'Andrew Bennett', 'Frank Lopez',
+ 'Benjamin Wood', 'Michelle Jenkins', 'Alice Lee',
+ 'Lois Gonzales', 'Diane Perez', 'Cheryl Torres',
+ 'Ernest Smith', 'Steve Mitchell', 'Barbara Jones',
+ 'Wanda Roberts', 'Julie Watson', 'Carlos Harris',
+ 'Anthony Phillips', 'Ralph Gray', 'Donna Hill',
+ 'Alan Coleman', 'Lawrence Ross', 'Stephen Flores',
+ 'Robert Simmons', 'Gloria White', 'Doris Wilson',
+ 'Shirley Sanders', 'Matthew Bell', 'Janice Hughes',
+ 'Walter Nelson', 'Gerald Taylor', 'Tammy Martin',
+ 'Gregory Barnes', 'Jonathan Baker', 'Lillian Green',
+ 'Brenda Hernandez', 'Denise Davis', 'Bobby Rogers',
+ 'Joe Lewis', 'Teresa Bailey', 'Craig Russell',
+ 'Angela Rivera', 'Rebecca Jackson', 'Nicole Henderson',
+ 'Kenneth James', 'Nicholas Bryant', 'Anne Washington',
+ 'Irene Miller', 'Theresa Martinez', 'Evelyn Sanchez',
+ 'Richard Anderson', 'Jeffrey Robinson', 'Heather Diaz',
+ 'Joshua Butler', 'Joan Peterson', 'Todd Campbell',
+ 'Timothy Kelly', 'Steven King', 'Norma Reed',
+ 'Carolyn Turner', 'Ruth Evans', 'Carol Thomas',
+ 'Arthur Howard', 'Peter Carter', 'Debra Ramirez',
+ 'Marie Walker', 'Donald Garcia', 'Janet Gonzalez',
+ 'Harold Adams', 'Bonnie Cook', 'Paula Long',
+ 'Bruce Griffin', 'Adam Hall' ,'Annie Young',
+ 'Jacqueline Alexander', 'Kimberly Edwards', 'Sarah Wright',
+ 'Terry Williams', 'Johnny Morris', 'Andrea Ward',
+ 'Margaret Allen', 'Sandra Price', 'Scott Foster',
+ 'Elizabeth Brown', 'Wayne Cooper', 'Mildred Brooks',
+ 'Dorothy Perry', 'Lori Powell', 'Kathryn Murphy',
+ 'Judy Johnson', 'Albert Morgan', 'William Richardson',
+ 'Randy Stewart', 'Roger Thompson', 'Anna Rodriguez',
+ )
+ """
+ print 'Munging customer names of open orders...'
+ for i in Order.objects.filter(state=Order.STATE_QUEUED):
+ if i.customer:
+ i.customer.name = choice(names)
+ i.customer.save()
+ """
+ print 'Munging technician names'
+ users = User.objects.exclude(username='filipp')
+ newnames = [x.split()[0].lower() for x in names]
+ oldnames = users.values_list("username", flat=True)
+ idx = 0
+
+ for i in users:
+ i.first_name, i.last_name = choice(names).split()
+ i.email = i.username + '@example.com'
+ i.save()
+
+ print 'Munging location names'
+ a = 65
+ for i in Location.objects.all():
+ #i.title = 'Location %s' % chr(a)
+ i.email = slugify(i.title) + '@example.com'
+ i.city = 'Cupertino'
+ i.phone = '0451 202 7' + str(a)
+ i.address = '1 Infinite Loop'
+ a += 1
+ i.save()
+
+ print 'Munging GSX account names'
+ a = 65
+ for i in GsxAccount.objects.all():
+ i.title = 'GSX Account %s' % chr(a)
+ a += 1
+ i.save()
+
diff --git a/servo/management/commands/slugifycategories.py b/servo/management/commands/slugifycategories.py
new file mode 100644
index 0000000..07ea3a1
--- /dev/null
+++ b/servo/management/commands/slugifycategories.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.utils.text import slugify
+from django.core.management.base import BaseCommand
+
+from servo.models import ProductCategory
+
+
+class Command(BaseCommand):
+ help = "Fixes ProductCategory slug fields"
+
+ def handle(self, *args, **options):
+ for i in ProductCategory.objects.all():
+ i.save()
diff --git a/servo/management/commands/sutostaff.py b/servo/management/commands/sutostaff.py
new file mode 100644
index 0000000..6450603
--- /dev/null
+++ b/servo/management/commands/sutostaff.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import logging
+from email.parser import Parser
+
+from django.core.management.base import BaseCommand, CommandError
+
+from servo.models import User
+
+
+class Command(BaseCommand):
+ help = "Converts SuperUsers to Staff"
+
+ def handle(self, *args, **options):
+ for u in User.objects.filter(is_superuser=True):
+ u.is_superuser = False
+ u.is_staff = True
+ u.save()
+ \ No newline at end of file
diff --git a/servo/management/commands/tokenize.py b/servo/management/commands/tokenize.py
new file mode 100644
index 0000000..fd5daa9
--- /dev/null
+++ b/servo/management/commands/tokenize.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.db.models import Q
+from django.utils import timezone
+from django.core.management.base import BaseCommand, CommandError
+
+from servo.models import User
+
+
+class Command(BaseCommand):
+ help = "Creates API token for user"
+
+ def handle(self, *args, **options):
+ user = User.objects.get(username=args[0])
+ print(user.create_token())
diff --git a/servo/management/commands/updatecomponents.py b/servo/management/commands/updatecomponents.py
new file mode 100644
index 0000000..75b6de5
--- /dev/null
+++ b/servo/management/commands/updatecomponents.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+
+from servo.models import User, GsxAccount, Product, ServicePart
+
+
+class Command(BaseCommand):
+ help = "Update missing component codes"
+
+ def handle(self, *args, **options):
+ u = User.objects.get(username='filipp')
+ GsxAccount.default(u)
+ for p in Product.objects.filter(component_code='').exclude(labour_tier=''):
+ try:
+ info = ServicePart(part_number=p.code).lookup()
+ p.component_code = info.componentCode.strip()
+ p.save()
+ except Exception, e:
+ print e
diff --git a/servo/management/commands/updateorders.py b/servo/management/commands/updateorders.py
new file mode 100644
index 0000000..caad815
--- /dev/null
+++ b/servo/management/commands/updateorders.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+
+from servo.models import Order
+
+
+class Command(BaseCommand):
+
+ help = "Updates order descriptions"
+
+ def handle(self, *args, **options):
+ for o in Order.objects.filter(description=""):
+ o.description = o.device_name() or ""
+ o.save()
+
+ for o in Order.objects.filter(status_name=""):
+ o.status_name = o.get_status_name() or ""
+ o.save()
+
+ for o in Order.objects.filter(customer_name=""):
+ o.customer_name = o.get_customer_name() or ""
+ o.save()
diff --git a/servo/management/commands/updatepototals.py b/servo/management/commands/updatepototals.py
new file mode 100644
index 0000000..ae8f7cd
--- /dev/null
+++ b/servo/management/commands/updatepototals.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+from servo.models import PurchaseOrder
+
+
+class Command(BaseCommand):
+
+ help = "Update Purchase Order totals"
+
+ def handle(self, *args, **options):
+ for po in PurchaseOrder.objects.all():
+ po.total = po.sum()
+ po.save()
diff --git a/servo/management/commands/updateprices.py b/servo/management/commands/updateprices.py
new file mode 100644
index 0000000..a916870
--- /dev/null
+++ b/servo/management/commands/updateprices.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import sys
+import logging
+
+from django.core.management.base import BaseCommand
+from servo.models import (Product, GsxAccount, User,)
+
+
+class Command(BaseCommand):
+ help = "Updates all part prices from GSX"
+
+ def handle(self, *args, **options):
+ if len(args) < 1:
+ print "Usage: updateprices username [start:finish]"
+ sys.exit(1)
+
+ start, counter = 0, 0
+ finish = 999999999999
+
+ if len(args) == 2:
+ start, finish = args[1].split(':')
+
+ GsxAccount.default(User.objects.get(username=args[0]))
+
+ products = Product.objects.filter(pk__gt=start, pk__lt=finish)
+ products = products.exclude(part_type='SERVICE')
+ products = products.exclude(fixed_price=True)
+
+ for i in products.order_by('id'):
+ logging.debug('Updating product %d' % i.pk)
+ try:
+ i.update_price()
+ i.save()
+ counter += 1
+ except Exception, e:
+ logging.debug(e)
+
+ print '%d product prices updated' % counter
diff --git a/servo/management/commands/updaterepairs.py b/servo/management/commands/updaterepairs.py
new file mode 100644
index 0000000..4eb1b58
--- /dev/null
+++ b/servo/management/commands/updaterepairs.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013, First Party Software
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+from django.core.management.base import BaseCommand
+
+from servo.models import Repair
+
+
+class Command(BaseCommand):
+
+ help = "Updates statuses and details of open GSX repairs"
+
+ def handle(self, *args, **options):
+ repairs = Repair.objects.filter(completed_at=None)
+
+ for r in repairs.exclude(confirmation=""):
+ r.connect_gsx()
+ try:
+ details = r.get_details()
+ r.update_details(details)
+ except Exception:
+ pass