Merge branch 'testing' into dpp

This commit is contained in:
Cayo Puigdefabregas 2023-06-13 12:32:08 +02:00
commit 80486136bd
9 changed files with 211 additions and 16 deletions

View file

@ -55,9 +55,11 @@ from ereuse_devicehub.resources.device.models import (
DataStorage, DataStorage,
Desktop, Desktop,
Device, Device,
HardDrive,
Keyboard, Keyboard,
Laptop, Laptop,
MemoryCardReader, MemoryCardReader,
Mobile,
Monitor, Monitor,
Mouse, Mouse,
Other, Other,
@ -65,6 +67,7 @@ from ereuse_devicehub.resources.device.models import (
Projector, Projector,
Server, Server,
Smartphone, Smartphone,
SolidStateDrive,
Tablet, Tablet,
TelevisionSet, TelevisionSet,
) )
@ -100,7 +103,7 @@ DEVICES = {
"Drives & Storage": [ "Drives & Storage": [
"All DataStorage", "All DataStorage",
"HardDrive", "HardDrive",
"SolidStageDrive", "SolidStateDrive",
], ],
"Accessories": [ "Accessories": [
"All Accessories", "All Accessories",
@ -128,6 +131,7 @@ MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"]
STORAGE = ["HardDrive", "SolidStateDrive"] STORAGE = ["HardDrive", "SolidStateDrive"]
ACCESSORIES = ["Mouse", "MemoryCardReader", "SAI", "Keyboard"] ACCESSORIES = ["Mouse", "MemoryCardReader", "SAI", "Keyboard"]
OTHERS = ["Other"] OTHERS = ["Other"]
DATASTORAGE = ['HardDrive', 'SolidStateDrive']
class AdvancedSearchForm(FlaskForm): class AdvancedSearchForm(FlaskForm):
@ -202,7 +206,7 @@ class FilterForm(FlaskForm):
# Generic Filters # Generic Filters
if "All Devices" == self.device_type: if "All Devices" == self.device_type:
filter_type = COMPUTERS + MONITORS + MOBILE + OTHERS filter_type = COMPUTERS + MONITORS + MOBILE + DATASTORAGE + OTHERS
elif "All Computers" == self.device_type: elif "All Computers" == self.device_type:
filter_type = COMPUTERS filter_type = COMPUTERS
@ -399,6 +403,7 @@ class NewDeviceForm(FlaskForm):
sku = StringField('SKU', [validators.Optional()]) sku = StringField('SKU', [validators.Optional()])
image = URLField('Image', [validators.Optional(), validators.URL()]) image = URLField('Image', [validators.Optional(), validators.URL()])
imei = IntegerField('IMEI', [validators.Optional()]) imei = IntegerField('IMEI', [validators.Optional()])
data_storage_size = FloatField('Storage Size', [validators.Optional()])
meid = StringField('MEID', [validators.Optional()]) meid = StringField('MEID', [validators.Optional()])
resolution = IntegerField('Resolution width', [validators.Optional()]) resolution = IntegerField('Resolution width', [validators.Optional()])
screen = FloatField('Screen size', [validators.Optional()]) screen = FloatField('Screen size', [validators.Optional()])
@ -418,6 +423,8 @@ class NewDeviceForm(FlaskForm):
"Smartphone": Smartphone, "Smartphone": Smartphone,
"Tablet": Tablet, "Tablet": Tablet,
"Cellphone": Cellphone, "Cellphone": Cellphone,
"HardDrive": HardDrive,
"SolidStateDrive": SolidStateDrive,
"ComputerMonitor": ComputerMonitor, "ComputerMonitor": ComputerMonitor,
"Monitor": Monitor, "Monitor": Monitor,
"TelevisionSet": TelevisionSet, "TelevisionSet": TelevisionSet,
@ -429,7 +436,7 @@ class NewDeviceForm(FlaskForm):
"Other": Other, "Other": Other,
} }
def reset_from_obj(self): def reset_from_obj(self): # noqa: C901
if not self._obj: if not self._obj:
return return
disabled = {'disabled': "disabled"} disabled = {'disabled': "disabled"}
@ -466,9 +473,13 @@ class NewDeviceForm(FlaskForm):
if self._obj.type in ['Smartphone', 'Tablet', 'Cellphone']: if self._obj.type in ['Smartphone', 'Tablet', 'Cellphone']:
self.imei.data = self._obj.imei self.imei.data = self._obj.imei
self.meid.data = self._obj.meid self.meid.data = self._obj.meid
self.data_storage_size.data = self._obj.data_storage_size
if self._obj.type == 'ComputerMonitor': if self._obj.type == 'ComputerMonitor':
self.resolution.data = self._obj.resolution_width self.resolution.data = self._obj.resolution_width
self.screen.data = self._obj.size self.screen.data = self._obj.size
if self._obj.type in ['HardDrive', 'SolidStateDrive']:
if self._obj.size:
self.data_storage_size.data = self._obj.size / 1000
if self._obj.placeholder.is_abstract: if self._obj.placeholder.is_abstract:
self.type.render_kw = disabled self.type.render_kw = disabled
@ -493,6 +504,9 @@ class NewDeviceForm(FlaskForm):
if self._obj.type in ['Smartphone', 'Tablet', 'Cellphone']: if self._obj.type in ['Smartphone', 'Tablet', 'Cellphone']:
self.imei.render_kw = disabled self.imei.render_kw = disabled
self.meid.render_kw = disabled self.meid.render_kw = disabled
self.data_storage_size.render_kw = disabled
if self._obj.type in ['HardDrive', 'SolidStateDrive']:
self.data_storage_size.render_kw = disabled
if self._obj.type == 'ComputerMonitor': if self._obj.type == 'ComputerMonitor':
self.resolution.render_kw = disabled self.resolution.render_kw = disabled
self.screen.render_kw = disabled self.screen.render_kw = disabled
@ -605,6 +619,11 @@ class NewDeviceForm(FlaskForm):
if self.type.data in ['Smartphone', 'Tablet', 'Cellphone']: if self.type.data in ['Smartphone', 'Tablet', 'Cellphone']:
device.imei = self.imei.data device.imei = self.imei.data
device.meid = self.meid.data device.meid = self.meid.data
device.data_storage_size = self.data_storage_size.data
if self.type.data in ['HardDrive', 'SolidStateDrive']:
if self.data_storage_size.data:
device.size = self.data_storage_size.data * 1000
device.image = URL(self.image.data) device.image = URL(self.image.data)
@ -673,6 +692,11 @@ class NewDeviceForm(FlaskForm):
if self._obj.type in ['Smartphone', 'Tablet', 'Cellphone']: if self._obj.type in ['Smartphone', 'Tablet', 'Cellphone']:
self._obj.imei = self.imei.data self._obj.imei = self.imei.data
self._obj.meid = self.meid.data self._obj.meid = self.meid.data
self._obj.data_storage_size = self.data_storage_size.data
if self.type.data in ['HardDrive', 'SolidStateDrive']:
if self.data_storage_size.data:
self._obj.size = self.data_storage_size.data * 1000
if ( if (
self.appearance.data self.appearance.data
@ -1085,6 +1109,14 @@ class DataWipeForm(ActionFormMixin):
del self.document del self.document
for dev in self._devices: for dev in self._devices:
ac = None ac = None
if isinstance(dev, Mobile) or isinstance(dev, DataStorage):
ac = Model()
self.populate_obj(ac)
ac.device_id = dev.id
ac.document = document.form._obj
db.session.add(ac)
continue
for hd in dev.components: for hd in dev.components:
if not isinstance(hd, DataStorage): if not isinstance(hd, DataStorage):
continue continue

View file

@ -47,6 +47,7 @@ from ereuse_devicehub.resources.device.models import (
Computer, Computer,
DataStorage, DataStorage,
Device, Device,
Mobile,
Placeholder, Placeholder,
) )
from ereuse_devicehub.resources.documents.device_row import ActionRow, DeviceRow from ereuse_devicehub.resources.documents.device_row import ActionRow, DeviceRow
@ -1198,6 +1199,10 @@ class ExportsView(View):
ac = device.last_erase_action ac = device.last_erase_action
if ac: if ac:
erasures.append(ac) erasures.append(ac)
elif isinstance(device, Mobile):
ac = device.last_erase_action
if ac:
erasures.append(ac)
return erasures return erasures
def get_costum_details(self, erasures): def get_costum_details(self, erasures):
@ -1241,15 +1246,19 @@ class ExportsView(View):
def get_server_erasure_hosts(self, erasures): def get_server_erasure_hosts(self, erasures):
erasures_host = [] erasures_host = []
erasures_mobile = []
erasures_on_server = [] erasures_on_server = []
for erase in erasures: for erase in erasures:
try: try:
if isinstance(erase.device, Mobile):
erasures_mobile.append(erase.device)
continue
if erase.parent.binding.kangaroo: if erase.parent.binding.kangaroo:
erasures_host.append(erase.parent) erasures_host.append(erase.parent)
erasures_on_server.append(erase) erasures_on_server.append(erase)
except Exception: except Exception:
pass pass
return erasures_host, erasures_on_server return erasures_host, erasures_on_server, erasures_mobile
def build_erasure_certificate(self): def build_erasure_certificate(self):
erasures = self.get_datastorages() erasures = self.get_datastorages()
@ -1261,9 +1270,10 @@ class ExportsView(View):
my_data, customer_details = self.get_costum_details(erasures) my_data, customer_details = self.get_costum_details(erasures)
a, b = self.get_server_erasure_hosts(erasures) a, b, c = self.get_server_erasure_hosts(erasures)
erasures_host, erasures_on_server = a, b erasures_host, erasures_on_server, erasures_mobile = a, b, c
erasures_host = set(erasures_host) erasures_host = set(erasures_host)
erasures_mobile = set(erasures_mobile)
result_success = 0 result_success = 0
result_failed = 0 result_failed = 0
@ -1278,7 +1288,8 @@ class ExportsView(View):
erasures_on_server = sorted(erasures_on_server, key=lambda x: x.end_time) erasures_on_server = sorted(erasures_on_server, key=lambda x: x.end_time)
erasures_normal = list(set(erasures) - set(erasures_on_server)) erasures_normal = list(set(erasures) - set(erasures_on_server))
erasures_normal = sorted(erasures_normal, key=lambda x: x.end_time) erasures_normal = sorted(erasures_normal, key=lambda x: x.end_time)
n_computers = len({x.parent for x in erasures} - erasures_host) n_computers = len({x.parent for x in erasures if x.parent} - erasures_host)
n_mobiles = len(erasures_mobile)
params = { params = {
'title': 'Device Sanitization', 'title': 'Device Sanitization',
@ -1289,10 +1300,12 @@ class ExportsView(View):
'software': software, 'software': software,
'my_data': my_data, 'my_data': my_data,
'n_computers': n_computers, 'n_computers': n_computers,
'n_mobiles': n_mobiles,
'result_success': result_success, 'result_success': result_success,
'result_failed': result_failed, 'result_failed': result_failed,
'customer_details': customer_details, 'customer_details': customer_details,
'erasure_hosts': erasures_host, 'erasure_hosts': erasures_host,
'erasure_mobiles': erasures_mobile,
'erasures_normal': erasures_normal, 'erasures_normal': erasures_normal,
} }
return flask.render_template('inventory/erasure.html', **params) return flask.render_template('inventory/erasure.html', **params)

View file

@ -1116,6 +1116,9 @@ class Device(Thing):
return lot return lot
return None return None
def is_mobile(self):
return isinstance(self, Mobile)
def __lt__(self, other): def __lt__(self, other):
return self.id < other.id return self.id < other.id
@ -1425,11 +1428,11 @@ class Computer(Device):
@property @property
def external_document_erasure(self): def external_document_erasure(self):
"""Returns the external ``DataStorage`` proof of erasure.""" """Returns the external ``DataStorage`` proof of erasure."""
from ereuse_devicehub.resources.action.models import DataWipe from ereuse_devicehub.resources.action.models import EraseDataWipe
urls = set() urls = set()
try: try:
ev = self.last_action_of(DataWipe) ev = self.last_action_of(EraseDataWipe)
urls.add(ev.document.url.to_text()) urls.add(ev.document.url.to_text())
except LookupError: except LookupError:
pass pass
@ -1540,6 +1543,48 @@ class Mobile(Device):
raise ValidationError('{} is not a valid meid.'.format(value)) raise ValidationError('{} is not a valid meid.'.format(value))
return value return value
@property
def last_erase_action(self):
erase_auto = None
erase_manual = None
if self.binding:
erase_auto = self.privacy
erase_manual = self.binding.device.privacy
if self.placeholder:
erase_manual = self.privacy
if self.placeholder.binding:
erase_auto = self.placeholder.binding.privacy
if erase_auto and erase_manual:
return (
erase_auto
if erase_auto.created > erase_manual.created
else erase_manual
)
if erase_manual:
return erase_manual
if erase_auto:
return erase_auto
return None
@property
def privacy(self):
"""Returns the privacy compliance state of the data storage.
This is, the last erasure performed to the data storage.
"""
from ereuse_devicehub.resources.action.models import EraseBasic
try:
ev = self.last_action_of(EraseBasic)
except LookupError:
ev = None
return ev
def get_size(self):
return self.data_storage_size
class Smartphone(Mobile): class Smartphone(Mobile):
pass pass

View file

@ -343,6 +343,10 @@ class DeviceRow(BaseDeviceRow):
if isinstance(device, d.Mobile): if isinstance(device, d.Mobile):
self['IMEI'] = device.imei or '' self['IMEI'] = device.imei or ''
self.get_erasure_datawipe_mobile(device)
if isinstance(device, d.DataStorage):
self.get_erasure_datawipe_mobile(device)
def components(self): def components(self):
"""Function to get all components information of a device.""" """Function to get all components information of a device."""
@ -417,6 +421,32 @@ class DeviceRow(BaseDeviceRow):
self['{} {} Size (MB)'.format(ctype, i)] = none2str(component.size) self['{} {} Size (MB)'.format(ctype, i)] = none2str(component.size)
self['{} {} Speed (MHz)'.format(ctype, i)] = none2str(component.speed) self['{} {} Speed (MHz)'.format(ctype, i)] = none2str(component.speed)
def get_erasure_datawipe_mobile(self, device):
actions = sorted(device.actions)
erasures = [a for a in actions if a.type == 'EraseDataWipe']
erasure = erasures[-1] if erasures else None
if erasure:
self['Erasure DataStorage 1'] = none2str(device.chid)
if isinstance(device, d.Mobile):
serial_number = none2str(device.imei)
size = device.data_storage_size
size = size * 1000 if size else 0
storage_size = none2str(size)
if isinstance(device, d.DataStorage):
serial_number = none2str(device.serial_number)
storage_size = none2str(device.size)
self['Erasure DataStorage 1 Serial Number'] = serial_number
self['Erasure DataStorage 1 Size (MB)'] = storage_size
self['Erasure DataStorage 1 Software'] = erasure.document.software
self['Erasure DataStorage 1 Result'] = get_result(erasure)
self['Erasure DataStorage 1 Type'] = erasure.type
self['Erasure DataStorage 1 Date'] = format(erasure.document.date or '')
self['Erasure DataStorage 1 Certificate URL'] = (
erasure.document.url and erasure.document.url.to_text() or ''
)
def get_datastorage(self, ctype, i, component): def get_datastorage(self, ctype, i, component):
"""Particular fields for component DataStorage. """Particular fields for component DataStorage.
A DataStorage can be HardDrive or SolidStateDrive. A DataStorage can be HardDrive or SolidStateDrive.
@ -455,6 +485,10 @@ class DeviceRow(BaseDeviceRow):
self['Erasure {} {} Size (MB)'.format(ctype, i)] = none2str(component.size) self['Erasure {} {} Size (MB)'.format(ctype, i)] = none2str(component.size)
self['Erasure {} {} Software'.format(ctype, i)] = erasure.document.software self['Erasure {} {} Software'.format(ctype, i)] = erasure.document.software
self['Erasure {} {} Result'.format(ctype, i)] = get_result(erasure) self['Erasure {} {} Result'.format(ctype, i)] = get_result(erasure)
self['Erasure {} {} Type'.format(ctype, i)] = erasure.type
self['Erasure {} {} Date'.format(ctype, i)] = format(
erasure.document.date or ''
)
self['Erasure {} {} Certificate URL'.format(ctype, i)] = ( self['Erasure {} {} Certificate URL'.format(ctype, i)] = (
erasure.document.url and erasure.document.url.to_text() or '' erasure.document.url and erasure.document.url.to_text() or ''
) )

View file

@ -1,28 +1,44 @@
$(document).ready(() => { $(document).ready(() => {
$("#type").on("change", deviceInputs); $("#type").on("change", deviceInputs2);
$("#amount").on("change", amountInputs); $("#amount").on("change", deviceInputs2);
deviceInputs2()
})
function deviceInputs2() {
deviceInputs(); deviceInputs();
amountInputs(); amountInputs();
}) }
function deviceInputs() { function deviceInputs() {
if ($("#type").val() == "ComputerMonitor") { if ($("#type").val() == "ComputerMonitor") {
$("#screen").show(); $("#screen").show();
$("#resolution").show(); $("#resolution").show();
$("#components2").hide();
$("#imei").hide(); $("#imei").hide();
$("#meid").hide(); $("#meid").hide();
$("#data_storage_size").hide();
} else if (["Smartphone", "Cellphone", "Tablet"].includes($("#type").val())) { } else if (["Smartphone", "Cellphone", "Tablet"].includes($("#type").val())) {
$("#screen").hide(); $("#screen").hide();
$("#resolution").hide(); $("#resolution").hide();
$("#components2").hide();
$("#imei").show(); $("#imei").show();
$("#meid").show(); $("#meid").show();
$("#data_storage_size").show();
} else if (["HardDrive", "SolidStateDrive"].includes($("#type").val())) {
$("#screen").hide();
$("#resolution").hide();
$("#components2").hide();
$("#imei").hide();
$("#meid").hide();
$("#data_storage_size").show();
} else { } else {
$("#screen").hide(); $("#screen").hide();
$("#resolution").hide(); $("#resolution").hide();
$("#imei").hide(); $("#imei").hide();
$("#meid").hide(); $("#meid").hide();
$("#data_storage_size").hide();
$("#components2").show();
}; };
amountInputs();
} }
function amountInputs() { function amountInputs() {
@ -35,6 +51,7 @@ function amountInputs() {
$("#Sku").hide(); $("#Sku").hide();
$("#imei").hide(); $("#imei").hide();
$("#meid").hide(); $("#meid").hide();
$("#data_storage_size").hide();
} else { } else {
$("#Phid").show(); $("#Phid").show();
$("#Id_device_supplier").show(); $("#Id_device_supplier").show();
@ -42,6 +59,5 @@ function amountInputs() {
$("#Serial_number").show(); $("#Serial_number").show();
$("#Part_number").show(); $("#Part_number").show();
$("#Sku").show(); $("#Sku").show();
deviceInputs();
}; };
} }

View file

@ -62,6 +62,12 @@
<option value="Cellphone" <option value="Cellphone"
{% if form.type.data == 'Cellphone' %} selected="selected"{% endif %}>Cellphone</option> {% if form.type.data == 'Cellphone' %} selected="selected"{% endif %}>Cellphone</option>
</optgroup> </optgroup>
<optgroup label="Data Storage">
<option value="HardDrive"
{% if form.type.data == 'HardDrive' %} selected="selected"{% endif %}>HardDrive</option>
<option value="SolidStateDrive"
{% if form.type.data == 'SolidStateDrive' %} selected="selected"{% endif %}>SolidStateDrive</option>
</optgroup>
<optgroup label="Computer Accessory"> <optgroup label="Computer Accessory">
<option value="Mouse" <option value="Mouse"
{% if form.type.data == 'Mouse' %} selected="selected"{% endif %}>Mouse</option> {% if form.type.data == 'Mouse' %} selected="selected"{% endif %}>Mouse</option>
@ -152,7 +158,7 @@
{% endif %} {% endif %}
</div> </div>
<div class="form-group mb-2"> <div id="components2" class="from-group mb-2">
<label for="label" class="form-label">{{ form.components.label }}</label> <label for="label" class="form-label">{{ form.components.label }}</label>
{{ form.components(class_="form-control") }} {{ form.components(class_="form-control") }}
<small class="text-muted form-text">Description of components</small> <small class="text-muted form-text">Description of components</small>
@ -434,6 +440,19 @@
{% endif %} {% endif %}
</div> </div>
<div id="data_storage_size" class="from-group mb-2">
<label for="data_storage_size" class="form-label">{{ form.data_storage_size.label }}</label>
{{ form.data_storage_size(class_="form-control") }}
<small class="text-muted form-text">Size in GB.</small>
{% if form.data_storage_size.errors %}
<p class="text-danger">
{% for error in form.data_storage_size.errors %}
{{ error }}<br/>
{% endfor %}
</p>
{% endif %}
</div>
<div id="resolution" class="from-group has-validation mb-2"> <div id="resolution" class="from-group has-validation mb-2">
<label for="resolution" class="form-label">{{ form.resolution.label }}</label> <label for="resolution" class="form-label">{{ form.resolution.label }}</label>
{{ form.resolution(class_="form-control") }} {{ form.resolution(class_="form-control") }}

View file

@ -209,6 +209,16 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% if n_mobiles %}
<tr style="padding-top:5px;">
<td style="width:20%;">
<span>N&deg; of mobiles:</span>
</td>
<td style="width:80%;">
<span>{{ n_mobiles }}</span>
</td>
</tr>
{% endif %}
<tr style="padding-top:5px;"> <tr style="padding-top:5px;">
<td style="width:20%;"> <td style="width:20%;">
<span>N&deg; of data storage unit(s):</span> <span>N&deg; of data storage unit(s):</span>
@ -322,12 +332,20 @@
<tbody> <tbody>
{% for erasure in erasures %} {% for erasure in erasures %}
<tr style="border-bottom: 1px dashed #000;"> <tr style="border-bottom: 1px dashed #000;">
{% if erasure.device.is_mobile() %}
<td>
IMEI:{{ (erasure.device.imei or '') }}
</td>
<td>
</td>
{% else %}
<td> <td>
{{ (erasure.device.serial_number or '').upper() }} {{ (erasure.device.serial_number or '').upper() }}
</td> </td>
<td> <td>
{{ (erasure.parent.serial_number or '').upper() }} {{ (erasure.parent.serial_number or '').upper() }}
</td> </td>
{% endif %}
<td> <td>
{{ erasure.get_public_name() }} {{ erasure.get_public_name() }}
</td> </td>
@ -351,12 +369,21 @@
<h3>Technical Details</h3> <h3>Technical Details</h3>
</div> </div>
{% endif %} {% endif %}
{% if erasure.device.is_mobile() %}
<h4>{{ (erasure.device.imei or '') }}</h4>
<dl>
<dt>Mobile Drive:</dt>
<dd>Model: {{ erasure.device.model }}</dd>
<dd>IMEI: {{ (erasure.device.imei or '') }}</dd>
<dd>DHID: {{ erasure.device.dhid }}</dd>
<dd>Size: {{ erasure.device.get_size() or '?' }} GB</dd>
{% else %}
<h4>{{ (erasure.device.serial_number or '').upper() }}</h4> <h4>{{ (erasure.device.serial_number or '').upper() }}</h4>
<dl> <dl>
<dt>Storage Drive:</dt> <dt>Storage Drive:</dt>
<dd>Model: {{ erasure.device.model }}</dd> <dd>Model: {{ erasure.device.model }}</dd>
<dd>SN: {{ (erasure.device.serial_number or '').upper() }}</dd> <dd>SN: {{ (erasure.device.serial_number or '').upper() }}</dd>
<dd>Size: {{ erasure.device.get_size()}}</dd> <dd>Size: {{ erasure.device.get_size() or '' }}</dd>
{% if erasure.parent %} {% if erasure.parent %}
<br /> <br />
<dt>Computer Host:</dt> <dt>Computer Host:</dt>
@ -364,6 +391,7 @@
<dd>SN: {{ (erasure.parent.serial_number or '').upper() }}</dd> <dd>SN: {{ (erasure.parent.serial_number or '').upper() }}</dd>
<dd>DHID: {{ erasure.parent.dhid }}</dd> <dd>DHID: {{ erasure.parent.dhid }}</dd>
{% endif %} {% endif %}
{% endif %}
<br /> <br />
<dt>Erasure:</dt> <dt>Erasure:</dt>

View file

@ -162,6 +162,7 @@
</thead> </thead>
<tbody> <tbody>
{% for ac in erasure.items %} {% for ac in erasure.items %}
{% if not ac.device.is_mobile() %}
<tr> <tr>
<td> <td>
<input type="checkbox" class="deviceSelect" data="{{ ac.device.my_partner.id }}" <input type="checkbox" class="deviceSelect" data="{{ ac.device.my_partner.id }}"
@ -227,6 +228,7 @@
</a> </a>
</td> </td>
</tr> </tr>
{% endif %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View file

@ -56,6 +56,12 @@
<option value="Cellphone" <option value="Cellphone"
{% if form.type.data == 'Cellphone' %} selected="selected"{% endif %}>Cellphone</option> {% if form.type.data == 'Cellphone' %} selected="selected"{% endif %}>Cellphone</option>
</optgroup> </optgroup>
<optgroup label="Data Storage">
<option value="HardDrive"
{% if form.type.data == 'HardDrive' %} selected="selected"{% endif %}>HardDrive</option>
<option value="SolidStateDrive"
{% if form.type.data == 'SolidStateDrive' %} selected="selected"{% endif %}>SolidStateDrive</option>
</optgroup>
<optgroup label="Computer Accessory"> <optgroup label="Computer Accessory">
<option value="Mouse" <option value="Mouse"
{% if form.type.data == 'Mouse' %} selected="selected"{% endif %}>Mouse</option> {% if form.type.data == 'Mouse' %} selected="selected"{% endif %}>Mouse</option>