Add a graph and calendar view,changed workflow,added delay calculation,restricted operations,changed all 'operation' tag to 'Work Order' added buttons in list view,removed extra delay field

bzr revid: nch@tinyerp.com-20080923102009-tnsfvdhhnvg7qdjl
This commit is contained in:
Naresh Choksy
2008-09-23 15:50:09 +05:30
parent 70da03a57f
commit 5a76e7c0c5
29 changed files with 264 additions and 65 deletions
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+18 -8
View File
@@ -3,21 +3,31 @@
<data noupdate="1">
<record model="mrp_operations.operation.code" id="mrp_op_1">
<field name="name">Normal Start</field>
<field name="code">nstart</field>
<field name="name">Start</field>
<field name="code">start</field>
<field name="start_stop">start</field>
</record>
<record model="mrp_operations.operation.code" id="mrp_op_2">
<field name="name">Normal Stop</field>
<field name="code">nstop</field>
<field name="start_stop">stop</field>
<field name="name">Pause</field>
<field name="code">pause</field>
<field name="start_stop">pause</field>
</record>
<record model="mrp_operations.operation.code" id="mrp_op_3">
<field name="name">Broken Stop</field>
<field name="code">broken</field>
<field name="start_stop">stop</field>
<field name="name">Cancel</field>
<field name="code">cancel</field>
<field name="start_stop">cancel</field>
</record>
<record model="mrp_operations.operation.code" id="mrp_op_4">
<field name="name">Resume</field>
<field name="code">resume</field>
<field name="start_stop">resume</field>
</record>
<record model="mrp_operations.operation.code" id="mrp_op_5">
<field name="name">Finished</field>
<field name="code">done</field>
<field name="start_stop">done</field>
</record>
</data>
+181 -32
View File
@@ -31,7 +31,7 @@
from osv import fields
from osv import osv
import ir
import datetime
import netsvc
import time
from mx import DateTime
@@ -50,41 +50,58 @@ class mrp_production_workcenter_line(osv.osv):
_inherit = 'mrp.production.workcenter.line'
_name = 'mrp.production.workcenter.line'
_description = 'Production workcenters used'
def _calc_delay(self, cr, uid, ids, name, arg, context={}):
result = {}
for obj in self.browse(cr, uid, ids, context):
if obj.date_start and obj.date_finnished:
diff = DateTime.strptime(obj.date_finnished, '%Y-%m-%d %H:%M:%S') - DateTime.strptime(obj.date_start, '%Y-%m-%d %H:%M:%S')
result[obj.id]=diff.day
else:
result[obj.id] = 0
return result
_columns = {
'state': fields.selection([('draft','Draft'),('confirm', 'Confirm'),('cancel','Canceled'),('done','Done')],'Status', readonly=True),
'date_start': fields.datetime('Start Date'),
'date_finnished': fields.datetime('End Date'),
'delay': fields.function(_calc_delay, method=True, string='Delay', help="This is delay between operation start and stop in this workcenter"),
'delay': fields.float('delay', required=True),
'state': fields.selection([('draft','Draft'),('startworking', 'In Progress'),('pause','Pause'),('cancel','Canceled'),('done','Finished')],'Status', readonly=True),
'date_start': fields.datetime('Start Date'),
'date_finnished': fields.datetime('End Date'),
'delay': fields.char('Delay',size=128,help="This is delay between operation start and stop in this workcenter",readonly=True),
'product':fields.related('production_id','product_id',type='many2one',relation='product.product',string='Product'),
'qty':fields.related('production_id','product_qty',type='float',string='Qty'),
'uom':fields.related('production_id','product_uom',type='many2one',relation='product.uom',string='UOM'),
}
_defaults = {
'state': lambda *a: 'draft',
'delay': lambda *a: 0.0
'delay': lambda *a: '0 Days 00 hrs and 00 mins'
}
def modify_production_order_state(self,cr,uid,ids,action):
wf_service = netsvc.LocalService("workflow")
oper_obj=self.browse(cr,uid,ids)[0]
prod_obj=self.pool.get('mrp.production').browse(cr,uid,[oper_obj.production_id.id])[0]
if action=='start':
if prod_obj.state =='ready':
wf_service.trg_validate(uid, 'mrp.production', prod_obj.id, 'button_produce', cr)
elif prod_obj.state =='in_production':
return
else:
raise osv.except_osv(_('Error!'),_('Production Order Cannot start in [%s] state')%(prod_obj.state,))
else:
oper_ids=self.search(cr,uid,[('production_id','=',prod_obj.id)])
obj=self.browse(cr,uid,oper_ids)
flag=True
for line in obj:
if line.state!='done':
flag=False
if flag:
wf_service.trg_validate(uid, 'mrp.production', oper_obj.production_id.id, 'button_produce_done', cr)
return
def action_draft(self, cr, uid, ids):
self.write(cr, uid, ids, {'state':'draft'})
# self.write(cr, uid, ids, {'state':'draft','date_start':None})
return True
def action_confirm(self, cr, uid, ids):
self.write(cr, uid, ids, {'state':'confirm'})
def action_start_working(self, cr, uid, ids):
self.modify_production_order_state(cr,uid,ids,'start')
self.write(cr, uid, ids, {'state':'startworking'})
# self.write(cr, uid, ids, {'state':'confirm','date_start':DateTime.now().strftime('%Y-%m-%d %H:%M:%S')})
return True
def action_done(self, cr, uid, ids):
self.write(cr, uid, ids, {'state':'done'})
self.modify_production_order_state(cr,uid,ids,'done')
# self.write(cr, uid, ids, {'state':'done','date_finnished':DateTime.now().strftime('%Y-%m-%d %H:%M:%S')})
return True
@@ -92,6 +109,16 @@ class mrp_production_workcenter_line(osv.osv):
self.write(cr, uid, ids, {'state':'cancel'})
# self.write(cr, uid, ids, {'state':'cancel','date_start':None})
return True
def action_pause(self, cr, uid, ids):
print "callled THE ACTUAL PAUSE"
self.write(cr, uid, ids, {'state':'pause'})
# self.write(cr, uid, ids, {'state':'draft','date_start':None})
return True
def action_resume(self, cr, uid, ids):
self.write(cr, uid, ids, {'state':'startworking'})
return True
mrp_production_workcenter_line()
@@ -100,18 +127,18 @@ class mrp_production(osv.osv):
_inherit = 'mrp.production'
_description = 'Production'
def action_confirm(self, cr, uid, ids):
obj=self.browse(cr,uid,ids)[0]
for workcenter_line in obj.workcenter_lines:
tmp=self.pool.get('mrp.production.workcenter.line').action_confirm(cr,uid,[workcenter_line.id])
return super(mrp_production,self).action_confirm(cr,uid,ids)
# def action_confirm(self, cr, uid, ids):
# obj=self.browse(cr,uid,ids)[0]
# for workcenter_line in obj.workcenter_lines:
# tmp=self.pool.get('mrp.production.workcenter.line').action_start_working(cr,uid,[workcenter_line.id])
# return super(mrp_production,self).action_confirm(cr,uid,ids)
def action_production_end(self, cr, uid, ids):
obj=self.browse(cr,uid,ids)[0]
for workcenter_line in obj.workcenter_lines:
tmp=self.pool.get('mrp.production.workcenter.line').action_done(cr,uid,[workcenter_line.id])
return super(mrp_production,self).action_production_end(cr,uid,ids)
#
def action_cancel(self, cr, uid, ids):
obj=self.browse(cr,uid,ids)[0]
for workcenter_line in obj.workcenter_lines:
@@ -125,7 +152,7 @@ class mrp_operations_operation_code(osv.osv):
_columns={
'name': fields.char('Operation Name',size=64, required=True),
'code': fields.char('Code', size=16, required=True),
'start_stop': fields.selection([('start','Start'),('stop','Stop'),('done','Done')], 'Status', required=True),
'start_stop': fields.selection([('start','Start'),('pause','Pause'),('resume','Resume'),('cancel','Cancel'),('done','Done')], 'Status', required=True),
}
mrp_operations_operation_code()
@@ -143,23 +170,142 @@ class mrp_operations_operation(osv.osv):
res[operation.id]=operation.production_id.date_planned
return res
def calc_delay(self,cr,uid,vals):
code_lst=[]
time_lst=[]
code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])])
code=self.pool.get('mrp_operations.operation.code').browse(cr,uid,code_ids)[0]
oper_ids=self.search(cr,uid,[('production_id','=',vals['production_id']),('workcenter_id','=',vals['workcenter_id'])])
oper_objs=self.browse(cr,uid,oper_ids)
for oper in oper_objs:
code_lst.append(oper.code_id.start_stop)
time_lst.append(oper.date_start)
code_lst.append(code.start_stop)
time_lst.append(vals['date_start'])
h=m=s=days=0
for i in range(0,len(code_lst)):
if code_lst[i]=='pause' or code_lst[i]=='done' or code_lst[i]=='cancel':
if code_lst[i]=='cancel' and code_lst[i-1]=='pause':
continue
a=datetime.datetime(int(time_lst[i-1][0:4]),int(time_lst[i-1][5:7]),int(time_lst[i-1][8:10]),int(time_lst[i-1][11:13]),int(time_lst[i-1][14:16]),int(time_lst[i-1][17:19]))
b=datetime.datetime(int(time_lst[i][0:4]),int(time_lst[i][5:7]),int(time_lst[i][8:10]),int(time_lst[i][11:13]),int(time_lst[i][14:16]),int(time_lst[i][17:19]))
diff =b-a
min, sec = divmod(diff.seconds, 60)
hrs, min = divmod(min, 60)
days+=diff.days
h+=hrs
m+=min
s+=sec
if s>=60:
m+=1
s=0
if m>=60:
h+=1
m=0
if h>=24:
days+=1
h=0
delay=str(days) +" Days "+ str(h) + " hrs and " + str(m)+ " mins"
return delay
def check_operation(self,cr,uid,vals):
code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])])
code=self.pool.get('mrp_operations.operation.code').browse(cr,uid,code_ids)[0]
code_lst = []
oper_ids=self.search(cr,uid,[('production_id','=',vals['production_id']),('workcenter_id','=',vals['workcenter_id'])])
oper_objs=self.browse(cr,uid,oper_ids)
if not oper_objs:
if code.start_stop!='start':
raise osv.except_osv(_('Sorry!'),_('Operation is not started yet !'))
return False
else:
for oper in oper_objs:
code_lst.append(oper.code_id.start_stop)
if code.start_stop=='start':
if 'start' in code_lst:
raise osv.except_osv(_('Sorry!'),_('Operation has already started !' 'You can either Pause /Finish/Cancel the operation'))
return False
if code.start_stop=='pause':
if code_lst[len(code_lst)-1]!='resume' and code_lst[len(code_lst)-1]!='start':
raise osv.except_osv(_('Error!'),_('You cannot Pause the Operation other then Start/Resume state !'))
return False
if code.start_stop=='resume':
if code_lst[len(code_lst)-1]!='pause':
raise osv.except_osv(_('Error!'),_(' You cannot Resume the operation other then Pause state !'))
return False
if code.start_stop=='done':
if code_lst[len(code_lst)-1]!='start' and code_lst[len(code_lst)-1]!='resume':
raise osv.except_osv(_('Sorry!'),_('You cannot finish the operation without Starting/Resuming it !'))
return False
if 'cancel' in code_lst:
raise osv.except_osv(_('Sorry!'),_('Operation is Already Cancelled !'))
return False
if code.start_stop=='cancel':
if not 'start' in code_lst :
raise osv.except_osv(_('Error!'),_('There is no Operation to be cancelled !'))
return False
if 'done' in code_lst:
raise osv.except_osv(_('Error!'),_('Operation is already finished !'))
return False
return True
def write(self, cr, uid, ids, vals, context=None):
oper_objs=self.browse(cr,uid,ids)[0]
vals['production_id']=oper_objs.production_id.id
vals['workcenter_id']=oper_objs.workcenter_id.id
if 'code_id' in vals:
self.check_operation(cr, uid, vals)
if 'date_start' in vals:
vals['date_start']=vals['date_start']
vals['code_id']=oper_objs.code_id.id
delay=self.calc_delay(cr, uid, vals)
wc_op_id=self.pool.get('mrp.production.workcenter.line').search(cr,uid,[('workcenter_id','=',vals['workcenter_id']),('production_id','=',vals['production_id'])])
self.pool.get('mrp.production.workcenter.line').write(cr,uid,wc_op_id,{'delay':delay})
return super(mrp_operations_operation, self).write(cr, uid, ids, vals, context=context)
def create(self, cr, uid, vals, context=None):
wf_service = netsvc.LocalService('workflow')
code_ids=self.pool.get('mrp_operations.operation.code').search(cr,uid,[('id','=',vals['code_id'])])
code=self.pool.get('mrp_operations.operation.code').browse(cr,uid,code_ids)[0]
if code.start_stop=='start' or code.start_stop=='stop' or code.start_stop=='done':
wc_op_id=self.pool.get('mrp.production.workcenter.line').search(cr,uid,[('workcenter_id','=',vals['workcenter_id']),('production_id','=',vals['production_id'])])
wc_op_id=self.pool.get('mrp.production.workcenter.line').search(cr,uid,[('workcenter_id','=',vals['workcenter_id']),('production_id','=',vals['production_id'])])
if code.start_stop in ('start','done','pause','cancel','resume'):
if not wc_op_id:
production_obj=self.pool.get('mrp.production').browse(cr,uid,vals['production_id'])
wc_op_id.append(self.pool.get('mrp.production.workcenter.line').create(cr,uid,{'production_id':vals['production_id'],'name':production_obj.product_id.name,'workcenter_id':vals['workcenter_id']}))
if code.start_stop=='start':
tmp=self.pool.get('mrp.production.workcenter.line').action_confirm(cr,uid,wc_op_id[0])
tmp=self.pool.get('mrp.production.workcenter.line').action_start_working(cr,uid,wc_op_id)
if code.start_stop=='done':
tmp=self.pool.get('mrp.production.workcenter.line').action_done(cr,uid,wc_op_id)
if code.start_stop=='stop':
self.pool.get('mrp.production').write(cr,uid,vals['production_id'],{'date_finnished':DateTime.now().strftime('%Y-%m-%d %H:%M:%S')})
if code.start_stop=='pause':
print "in PAUSE"
tmp=self.pool.get('mrp.production.workcenter.line').action_pause(cr,uid,wc_op_id)
if code.start_stop=='resume':
tmp=self.pool.get('mrp.production.workcenter.line').action_resume(cr,uid,wc_op_id)
if code.start_stop=='cancel':
tmp=self.pool.get('mrp.production.workcenter.line').action_cancel(cr,uid,wc_op_id)
if not self.check_operation(cr, uid, vals):
return
delay=self.calc_delay(cr, uid, vals)
self.pool.get('mrp.production.workcenter.line').write(cr,uid,wc_op_id,{'delay':delay})
return super(mrp_operations_operation, self).create(cr, uid, vals, context=context)
_columns={
'production_id':fields.many2one('mrp.production','Production',required=True),
'workcenter_id':fields.many2one('mrp.workcenter','Workcenter',required=True),
@@ -168,6 +314,9 @@ class mrp_operations_operation(osv.osv):
'date_finished': fields.datetime('End Date'),
'order_date': fields.function(_get_order_date,method=True,string='Order Date',type='date',store={'mrp.production':(['date_planned'],_order_date_search_production)}),
}
_defaults={
'date_start': lambda *a:DateTime.now().strftime('%Y-%m-%d %H:%M:%S')
}
mrp_operations_operation()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
View File
+38 -22
View File
@@ -9,6 +9,11 @@
<field name="type">tree</field>
<field name="arch" type="xml">
<field name="hour" position="after">
<button name="button_done" string="Finished" states="startworking"/>
<button name="button_start_working" string="Start working" states="draft,pause"/>
<field name="uom"/>
<field name="qty"/>
<field name="product"/>
<field name="production_id"/>
<field name="state" />
</field>
@@ -22,19 +27,17 @@
<field name="inherit_id" ref="mrp.mrp_production_workcenter_form_view"/>
<field name="arch" type="xml">
<field name="hour" position="after">
<group colspan="4" col="6">
<group colspan="8" col="8">
<field name="state" select="2"/>
<button name="button_draft" string="Set Draft" states="cancel"/>
<button name="button_confirm" string="Confirm" states="draft"/>
<button name="button_done" string="Done" states="confirm"/>
<button name="button_cancel" string="Cancel" states="draft,confirm"/>
<button name="button_start_working" string="Start working" states="draft"/>
<button name="button_resume" string="Resume" states="pause"/>
<button name="button_done" string="Finished" states="startworking"/>
<button name="button_pause" string="Pause" states="startworking"/>
<button name="button_cancel" string="Cancel" states="draft,startworking"/>
</group>
<field name="delay"/>
<field name="production_id"/>
<!-- <field name="date_finnished"/>
<field name="date_start"/>
<newline/>
-->
</field>
</field>
</record>
@@ -42,7 +45,7 @@
<!-- Action for tree view of workcenter line -->
<record model="ir.actions.act_window" id="mrp_production_wc_action_form">
<field name="name">Operations</field>
<field name="name">Work Order</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.production.workcenter.line</field>
<field name="view_type">form</field>
@@ -53,45 +56,45 @@
<!-- Action for All Operations -->
<record model="ir.actions.act_window" id="mrp_production_wc_action">
<field name="name">All Operations</field>
<field name="name">All Work Order</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.production.workcenter.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,graph</field>
</record>
<!-- Action for Confirm Operations -->
<record model="ir.actions.act_window" id="mrp_production_wc_confirm_action">
<field name="name">Confirm Operations</field>
<field name="name">Confirm Work Order</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.production.workcenter.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,graph</field>
<field name="domain">[('state','=','confirm')]</field>
</record>
<!-- Action for Draft Operations -->
<record model="ir.actions.act_window" id="mrp_production_wc_draft_action">
<field name="name">Draft Operations</field>
<field name="name">Draft Work Order</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.production.workcenter.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">tree,form,graph</field>
<field name="domain">[('state','=','draft')]</field>
</record>
<record model="ir.values" id="mrp_production_wc_action_open">
<field name="key2" eval="'tree_but_open'"/>
<field name="model" eval="'mrp.production.workcenter.line'"/>
<field name="name">Operations</field>
<field name="name">Work Order</field>
<field name="value" eval="'ir.actions.act_window,'+str(mrp_production_wc_action_form)"/>
<field name="object" eval="True"/>
</record>
<menuitem name="All Operations" parent="mrp.menu_mrp_root" id="menu_mrp_production_wc_action" action="mrp_production_wc_action"/>
<menuitem name="All Work Order" parent="mrp.menu_mrp_root" id="menu_mrp_production_wc_action" action="mrp_production_wc_action"/>
<menuitem name="Production Orders to Do" parent="menu_mrp_production_wc_action" action="mrp_production_wc_confirm_action" id="menu_mrp_production_wc_confirm_action"/>
<menuitem name="Waiting Production Orders" parent="menu_mrp_production_wc_action" action="mrp_production_wc_draft_action" id="menu_mrp_production_wc_draft_action"/>
@@ -124,7 +127,7 @@
</record>
<record model="ir.actions.act_window" id="mrp_production_code_action">
<field name="name">Operation Code</field>
<field name="name">Operations Code</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp_operations.operation.code</field>
<field name="view_type">form</field>
@@ -152,6 +155,20 @@
</field>
</record>
<record id="graph_in_hrs_workcenter" model="ir.ui.view">
<field name="name">graph.in.hrs.workcenter</field>
<field name="model">mrp.production.workcenter.line</field>
<field name="type">graph</field>
<field name="arch" type="xml">
<graph string="Graph view in hours by workcenter" type="bar">
<field name="workcenter_id"/>
<field name="hour" operator="+"/>
</graph>
</field>
</record>
<!--<record model="ir.ui.view" id="mrp_production_operation_form_view">
<field name="name">mrp.production.operation.form</field>
<field name="model">mrp_operations.operation</field>
@@ -166,14 +183,15 @@
</record>
-->
<record model="ir.actions.act_window" id="mrp_production_operation_action">
<field name="name">Operation</field>
<field name="name">Operations</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp_operations.operation</field>
<field name="view_type">form</field>
<field name="view_mode">tree,calendar,form</field>
<field name="view_id" ref="mrp_production_operation_tree_view"/>
</record>
<menuitem name="Operations" parent="mrp.menu_mrp_configuration" id="menu_mrp_production_operation_action" action="mrp_production_operation_action"/>
<menuitem name="Operations to Manufacturing" parent="mrp.menu_mrp_configuration" id="menu_mrp_production_operation_main"/>
<menuitem name="Operations" parent="menu_mrp_production_operation_main" id="menu_mrp_production_operation_action" action="mrp_production_operation_action"/>
<record model="ir.ui.view" id="operation_calendar_view">
<field name="name">mrp.perations.calendar</field>
@@ -182,8 +200,6 @@
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<calendar string="Calendar View" date_start="order_date" color="workcenter_id">
<field name="code_id"/>
<field name="production_id"/>
<field name="date_start"/>
<field name="date_finished"/>
</calendar>
+27 -3
View File
@@ -20,9 +20,9 @@
</record>
<record model="workflow.activity" id="prod_act_wc_start">
<field name="wkf_id" ref="wkf_prod_workcenter"/>
<field name="name">confirm</field>
<field name="name">startworking</field>
<field name="kind">function</field>
<field name="action">action_confirm()</field>
<field name="action">action_start_working()</field>
</record>
<record model="workflow.activity" id="prod_act_wc_cancel">
<field name="wkf_id" ref="wkf_prod_workcenter"/>
@@ -30,6 +30,18 @@
<field name="kind">function</field>
<field name="action">action_cancel()</field>
</record>
<record model="workflow.activity" id="prod_act_wc_pause">
<field name="wkf_id" ref="wkf_prod_workcenter"/>
<field name="name">pause</field>
<field name="kind">function</field>
<field name="action">action_pause()</field>
</record>
<record model="workflow.activity" id="prod_act_wc_resume">
<field name="wkf_id" ref="wkf_prod_workcenter"/>
<field name="name">resume</field>
<field name="kind">function</field>
<field name="action">action_resume()</field>
</record>
<record model="workflow.activity" id="prod_act_wc_done">
<field name="wkf_id" ref="wkf_prod_workcenter"/>
<field name="name">done</field>
@@ -37,6 +49,7 @@
<field name="flow_stop">True</field>
<field name="action">action_done()</field>
</record>
# -----------------------------------------------------------
# Transition
# -----------------------------------------------------------
@@ -44,7 +57,7 @@
<record model="workflow.transition" id="prod_trans_wc_draft_start">
<field name="act_from" ref="prod_act_wc_draft"/>
<field name="act_to" ref="prod_act_wc_start"/>
<field name="signal">button_confirm</field>
<field name="signal">button_start_working</field>
</record>
<record model="workflow.transition" id="prod_trans_wc_start_done">
<field name="act_from" ref="prod_act_wc_start"/>
@@ -66,5 +79,16 @@
<field name="act_to" ref="prod_act_wc_cancel"/>
<field name="signal">button_cancel</field>
</record>
<record model="workflow.transition" id="prod_trans_wc_start_pause">
<field name="act_from" ref="prod_act_wc_start"/>
<field name="act_to" ref="prod_act_wc_pause"/>
<field name="signal">button_pause</field>
</record>
<record model="workflow.transition" id="prod_trans_wc_pause_resume">
<field name="act_from" ref="prod_act_wc_pause"/>
<field name="act_to" ref="prod_act_wc_resume"/>
<field name="signal">button_resume</field>
</record>
</data>
</openerp>
View File
View File
View File
View File
View File
View File