<?php
namespace app\mobile\controller;
use think\facade\Db;
use think\facade\Filesystem;
use think\facade\Log;

class OmsMaterialReturn extends OmsBase {
    public function messageItems($account,$since,\app\mobile\service\MessageInbox $inbox) {
        if(!$inbox->allowsSource($this))$this->fail('接口不存在',404);
        if(!(int)$account['admin_id'])return [];$aid=(int)$account['admin_id'];$items=[];
        $rows=Db::name('common_outplant_back')->where('schema_version',1)->where(function($q)use($aid,$since){$q->where(['apv'=>$aid,'status'=>1])->whereOr(function($done)use($aid,$since){$done->where('apv',$aid)->where('apv_time','>=',$since); })->whereOr(function($own)use($aid,$since){$own->where('owner',$aid)->whereIn('status',[0,2])->where('apv_time','>=',$since);});})->select()->toArray();
        foreach($rows as $row){$p=$this->present($row,$account);$fields=['登记人'=>$p['ownerName'],'原出厂单'=>'#'.$row['o_id'],'返厂日期'=>$p['actualTime'],'物品说明'=>$p['source']['reason']];$route='/pages/material-return/detail?id='.$row['id'];
            if($p['canApprove'])$items[]=\app\mobile\service\MessageInbox::item('material-return',$row['id'],'verify','返厂登记待核验','待核验',$fields,$route,'pending',$row['create_time']);
            if((int)$row['apv']===$aid && (int)$row['apv_time']>0 && in_array((int)$row['status'],[0,2],true))$items[]=\app\mobile\service\MessageInbox::item('material-return',$row['id'],'verify','返厂核验已处理',$p['statusName'],$fields,$route,'done',$row['apv_time']);
            if((int)$row['owner']===$aid && in_array((int)$row['status'],[0,2],true))$items[]=\app\mobile\service\MessageInbox::item('material-return',$row['id'],'result-'.$row['status'],'返厂登记'.$p['statusName'],$p['statusName'],$fields+['核验意见'=>$row['apv_mark']],$route,'notice',$row['apv_time']);
        }return $items;
    }
    private function result($data=null){return json(['code'=>0,'message'=>'成功','data'=>$data]);}
    private function id($value){if(!is_scalar($value)||!preg_match('/^[1-9][0-9]{0,9}$/',(string)$value)||(float)$value>2147483647)$this->fail('编号不正确');return (int)$value;}
    private function text($value,$length,$optional=false){if(!is_string($value)||mb_strlen($value)>$length||(!$optional&&trim($value)===''))$this->fail('填写内容不正确');return trim($value);}
    private function admin($account){$row=(int)$account['admin_id']>0?Db::name('admin_user')->where(['id'=>$account['admin_id'],'username'=>$account['username'],'status'=>1])->find():null;if(!$row)$this->fail('需要有效后台人员关联',403);return $row;}
    // 定点8位：原数量最多10字符，缩放后处于64位整数范围；不使用浮点累加。
    private function quantity($value){if(!is_string($value)&&!is_int($value))$this->fail('数量格式不正确');$value=(string)$value;if(!preg_match('/^[0-9]{1,10}(\.[0-9]{1,8})?$/',$value))$this->fail('数量格式不正确，请核对原物料');$parts=explode('.',$value);return (int)$parts[0]*100000000+(int)str_pad($parts[1]??'',8,'0');}
    private function decimal($value){return (string)intdiv($value,100000000).'.'.str_pad((string)($value%100000000),8,'0',STR_PAD_LEFT);}
    private function displayQuantity($value){return rtrim(rtrim($this->decimal($value),'0'),'.');}
    private function image($path){$path=str_replace('\\','/',(string)$path);return strpos($path,'/storage/')===0&&strpos($path,'..')===false?'https://www.cqoms.com'.$path:'';}
    private function source($id){$row=Db::name('common_outplant')->where(['id'=>$this->id($id),'type'=>2])->find();if(!$row)$this->fail('原出厂单不存在',404);return $row;}
    private function department($out){return (int)$out['dpt']>0?(int)$out['dpt']:((int)$out['ownerType']>1?(int)Db::name('admin_user')->where('id',$out['owner'])->value('dpt'):0);}
    private function sameDepartment($out,$admin){return (int)$admin['dpt']>0&&$this->department($out)===(int)$admin['dpt'];}
    public function summaryFor($out,$account){
        $admin=$this->admin($account);$rows=Db::name('common_outplant_back')->where('o_id',$out['id'])->where('status','>=',0)->select()->toArray();$legacy=false;$pending=false;$approved=[];
        foreach($rows as $row){if((int)$row['schema_version']!==1)$legacy=true;if((int)$row['status']===1)$pending=true;if((int)$row['status']===2&&(int)$row['schema_version']===1)$approved[]=$row['id'];}
        $totals=$approved?array_column(Db::name('common_outplant_back_material')->whereIn('return_id',$approved)->field('source_material_id,SUM(num) total')->group('source_material_id')->select()->toArray(),'total','source_material_id'):[];
        $materials=[];$complete=true;$any=false;$invalid=false;
        foreach(Db::name('common_outplant_material')->where(['n_id'=>$out['id'],'item'=>1])->order('id')->select()->toArray() as $material){
            try{$quantity=$this->quantity($material['num']);$done=$this->quantity($totals[$material['id']]??'0');if($quantity<=0||$done>$quantity)throw new \RuntimeException('Invalid quantity');}
            catch(\Throwable $error){$invalid=true;$quantity=0;$done=0;}
            $left=max(0,$quantity-$done);if($left>0)$complete=false;if($done>0)$any=true;
            $materials[]=['id'=>(int)$material['id'],'name'=>$material['name'],'unit'=>$material['unit'],'type'=>(int)$material['type'],'original'=>$this->displayQuantity($quantity),'returned'=>$this->displayQuantity($done),'remaining'=>$this->displayQuantity($left),'image'=>$this->image($material['img'])];
        }
        $reason='';if((int)$out['backType']!==2)$reason=(int)$out['backType']===1?'无需返厂':'历史返厂标记待核对';elseif((int)$out['ownerType']<=1)$reason='历史原申请人身份需核对';elseif((int)$out['status']!==4||empty($out['check_time']))$reason='原单尚未放行或已失效';elseif(!$this->sameDepartment($out,$admin))$reason='仅本部门可登记';elseif($legacy)$reason='历史返厂待核对';elseif($invalid||!$materials)$reason='原物料或数量待核对';elseif($pending)$reason='已有待核验返厂申请';elseif($complete)$reason='已全部返厂';
        $status=(int)$out['backType']===1?3:($complete&&$materials&&!$invalid&&!$legacy?3:($any?2:1));
        return ['id'=>(int)$out['id'],'reason'=>(string)$out['reason'],'ownerName'=>Db::name('admin_user')->where('id',$out['owner'])->value('truename')?:'人员已变更','releaseTime'=>empty($out['check_time'])?'':date('Y-m-d H:i',$out['check_time']),'historicalDepartment'=>empty($out['dpt']),'returnStatus'=>$status,'returnStatusName'=>(int)$out['backType']===1?'无需返厂':($legacy?'历史返厂待核对':[1=>'待返厂',2=>'部分返厂',3=>'返厂完成'][$status]),'pending'=>$pending,'canRegister'=>$reason==='','blockReason'=>$reason,'materials'=>$materials];
    }
    public function options(){return $this->guarded(function(){$this->requireMethod('GET');$account=$this->current();$admin=$this->admin($account);return $this->result(['approvers'=>(int)$admin['dpt']>0?Db::name('admin_user')->where(['dpt'=>$admin['dpt'],'status'=>1])->where('id','<>',$admin['id'])->field('id,truename as name')->order('job,id')->select()->toArray():[]]);});}
    public function sources(){return $this->guarded(function(){
        $this->requireMethod('GET');$account=$this->current();$admin=$this->admin($account);$page=$this->id($this->request->get('page',1));$keyword=$this->text($this->request->get('keyword',''),50,true);
        if((int)$admin['dpt']<=0)return $this->result(['items'=>[],'total'=>0,'page'=>$page]);$ids=Db::name('admin_user')->where('dpt',$admin['dpt'])->column('id');$query=Db::name('common_outplant')->where(['type'=>2,'backType'=>2,'status'=>4])->where('ownerType','>',1)->where('check_time','>',0)->whereIn('return_status',[1,2]);
        $query->where(function($q)use($admin,$ids){$q->where('dpt',$admin['dpt'])->whereOr(function($s)use($ids){$s->where(function($t){$t->whereNull('dpt')->whereOr('dpt',0);})->whereIn('owner',$ids?:[-1]);});});
        $blocked=Db::name('common_outplant_back')->where('status','>=',0)->where(function($q){$q->where('schema_version',0)->whereOr('status',1);})->column('o_id');if($blocked)$query->whereNotIn('id',$blocked);
        if($keyword!==''){$people=Db::name('admin_user')->where('truename','like','%'.$keyword.'%')->column('id');$materials=Db::name('common_outplant_material')->where('item',1)->where('name','like','%'.$keyword.'%')->column('n_id');$query->where(function($q)use($keyword,$people,$materials){$q->where('reason','like','%'.$keyword.'%');if(ctype_digit($keyword))$q->whereOr('id',(int)$keyword);if($people)$q->whereOr('owner','in',$people);if($materials)$q->whereOr('id','in',$materials);});}
        $total=(clone $query)->count();$rows=$query->order('id desc')->page($page,10)->select()->toArray();return $this->result(['items'=>array_map(function($row)use($account){return $this->summaryFor($row,$account);},$rows),'total'=>$total,'page'=>$page]);
    });}
    public function sourceDetail(){return $this->guarded(function(){$this->requireMethod('GET');$account=$this->current();$admin=$this->admin($account);$out=$this->source($this->request->get('id'));if(!$this->sameDepartment($out,$admin))$this->fail('仅本部门可查看返厂来源',403);return $this->result($this->summaryFor($out,$account));});}
    private function visible($row,$out,$admin){return $this->sameDepartment($out,$admin)||in_array((int)$admin['id'],[(int)$row['owner'],(int)$row['apv'],(int)$out['owner']],true);}
    private function present($row,$account){$admin=$this->admin($account);$out=Db::name('common_outplant')->where(['id'=>$row['o_id'],'type'=>2])->find();$missing=!$out;if(!$out)$out=['id'=>$row['o_id'],'dpt'=>$row['dpt'],'owner'=>0,'ownerType'=>2];if(!$this->visible($row,$out,$admin))$this->fail('无权查看返厂单',403);$legacy=(int)$row['schema_version']!==1;
        $materials=$legacy?Db::name('common_outplant_material')->where(['item'=>2,'n_id'=>$row['id']])->select()->toArray():Db::name('common_outplant_back_material')->where('return_id',$row['id'])->select()->toArray();foreach($materials as &$material)$material['image']=$this->image($material['img']);unset($material);
        $row['id']=(int)$row['id'];$row['allBack']=(int)$row['allBack'];$row['status']=(int)$row['status'];$row['legacy']=$legacy;$row['statusName']=[0=>'已驳回',1=>'待核验',2=>'已核验'][$row['status']]??'已删除';$row['ownerName']=Db::name('admin_user')->where('id',$row['owner'])->value('truename')?:'人员已变更';$row['approverName']=Db::name('admin_user')->where('id',$row['apv'])->value('truename')?:'人员已变更';$row['createdAt']=date('Y-m-d H:i',$row['create_time']);$row['actualTime']=empty($row['actual_back_time'])?'未留存':date('Y-m-d',$row['actual_back_time']);$row['approvedAt']=empty($row['apv_time'])?'':date('Y-m-d H:i',$row['apv_time']);$row['materials']=$materials;$row['source']=$missing?['id'=>(int)$row['o_id'],'reason'=>'原出厂单缺失或类型异常','ownerName'=>'待核对','releaseTime'=>'','returnStatus'=>1,'returnStatusName'=>'历史返厂待核对','pending'=>false,'canRegister'=>false,'blockReason'=>'原单待核对','historicalDepartment'=>false,'materials'=>[]]:$this->summaryFor($out,$account);$row['canApprove']=!$missing&&!$legacy&&$row['status']===1&&(int)$row['apv']===(int)$admin['id']&&(int)$row['owner']!==(int)$admin['id']&&empty($row['apv_time']);$row['canDelete']=!$legacy&&empty($row['apv_time'])&&$row['status']===1&&(int)$row['owner']===(int)$admin['id'];return $row;}
    public function records(){return $this->guarded(function(){$this->requireMethod('GET');$account=$this->current();$admin=$this->admin($account);$scope=$this->request->get('scope','mine');if(!in_array($scope,['mine','approve'],true))$this->fail('列表类型不正确');$page=$this->id($this->request->get('page',1));$q=Db::name('common_outplant_back')->where($scope==='mine'?'owner':'apv',$admin['id'])->where('status','>=',0);$total=(clone $q)->count();return $this->result(['items'=>array_map(function($row)use($account){return $this->present($row,$account);},$q->order('id desc')->page($page,10)->select()->toArray()),'total'=>$total,'page'=>$page]);});}
    public function detail(){return $this->guarded(function(){$this->requireMethod('GET');$account=$this->current();$this->admin($account);$row=Db::name('common_outplant_back')->where('id',$this->id($this->request->get('id')))->where('status','>=',0)->find();if(!$row)$this->fail('返厂单不存在',404);return $this->result($this->present($row,$account));});}
    private function writeLock($action){$p=$this->app->config->get('database.connections.'.$this->app->config->get('database.default').'.prefix');if(!preg_match('/^[a-zA-Z0-9_]*$/',$p))throw new \RuntimeException('Invalid prefix');Db::query('SELECT 1');$c=Db::connect()->getConnection();$pdo=$c->getPdo();$pdo->exec('LOCK TABLES `'.$p.'common_outplant` WRITE, `'.$p.'common_outplant_material` READ, `'.$p.'common_outplant_back` WRITE, `'.$p.'common_outplant_back_material` WRITE, `'.$p.'admin_user` READ');try{return $action();}finally{$c->free();$pdo->exec('UNLOCK TABLES');}}
    protected function stagingDirectory(){return $this->app->getRuntimePath().'material-return-uploads/';}
    public function upload(){return $this->guarded(function(){$this->requireMethod('POST');$account=$this->current();$admin=$this->admin($account);$out=$this->source($this->request->post('sourceId'));$summary=$this->summaryFor($out,$account);if(!$summary['canRegister'])$this->fail($summary['blockReason'],409);$file=$this->request->file('image');if(!$file instanceof \think\file\UploadedFile||!$file->isValid()||$file->getSize()>5*1024*1024)$this->fail('图片不能超过5MB');$types=['image/jpeg'=>'jpg','image/png'=>'png','image/webp'=>'webp'];$mime=$file->getMime();if(!isset($types[$mime])||!@getimagesize($file->getPathname()))$this->fail('请选择真实图片');$dir=$this->stagingDirectory();if(!is_dir($dir)&&!mkdir($dir,0700,true)&&!is_dir($dir))$this->fail('图片暂存失败',503);foreach(glob($dir.'*')?:[] as $old)if(preg_match('/^[a-f0-9]{48}\.(jpg|png|webp)$/',basename($old))&&is_file($old)&&filemtime($old)<time()-86400)@unlink($old);$ref=bin2hex(random_bytes(24));$name=$ref.'.'.$types[$mime];$file->move($dir,$name);try{$this->cache()->set($this->cacheKey('return:upload:'.$ref),['username'=>$account['username'],'sourceId'=>(int)$out['id'],'name'=>$name],86400);}catch(\Throwable $e){@unlink($dir.$name);throw $e;}return $this->result(['reference'=>$ref]);});}
    public function create(){return $this->guarded(function(){
        $this->requireMethod('POST');$account=$this->current();$this->admin($account);$key='return:'.substr(hash('sha256',$this->cacheKey($account['username'])),0,48);$lock=Db::query('SELECT GET_LOCK(?,5) acquired',[$key]);if((int)$lock[0]['acquired']!==1)$this->fail('提交处理中',409);
        try{$submission=$this->text($this->request->post('submissionId'),64);$receiptKey=$this->cacheKey('return:receipt:'.hash('sha256',$account['username'].':'.$submission));$hash=hash('sha256',json_encode($this->request->post()));$receipt=$this->cache()->get($receiptKey);if($receipt){if($receipt['hash']!==$hash)$this->fail('提交标识重复',409);return $this->result(['id'=>(int)$receipt['id']]);}$paths=[];$uploads=[];
            try{$row=$this->writeLock(function()use($account,&$paths,&$uploads){$admin=$this->admin($account);$out=$this->source($this->request->post('sourceId'));$summary=$this->summaryFor($out,$account);if(!$summary['canRegister'])$this->fail($summary['blockReason'],409);$apv=$this->id($this->request->post('approverId'));if($apv===(int)$admin['id']||!Db::name('admin_user')->where(['id'=>$apv,'status'=>1,'dpt'=>$admin['dpt']])->find())$this->fail('请选择本部门其他启用人员核验');$time=$this->text($this->request->post('actualTime'),10);$date=\DateTime::createFromFormat('!Y-m-d',$time);if(!$date||$date->format('Y-m-d')!==$time||$time>date('Y-m-d')||$time<date('Y-m-d',$out['check_time']))$this->fail('返厂日期不能早于放行日期或晚于今天');$raw=$this->request->post('materials');if(!is_array($raw)||!$raw||count($raw)>100)$this->fail('请填写返厂物料');$source=array_column($summary['materials'],null,'id');$items=[];
                foreach($raw as $item){if(!is_array($item))$this->fail('物料不正确');$id=$this->id($item['sourceMaterialId']??null);if(!isset($source[$id])||isset($items[$id]))$this->fail('物料来源不正确或重复');$quantity=$this->quantity($item['num']??'');if($quantity<=0||$quantity>$this->quantity($source[$id]['remaining']))$this->fail('本次数量超过剩余应返数量');$ref=$item['reference']??'';$image='';if(!is_string($ref))$this->fail('图片引用不正确');if($ref!==''){if(!preg_match('/^[a-f0-9]{48}$/',$ref)||isset($uploads[$ref]))$this->fail('图片引用缺失或重复');$upload=$this->cache()->get($this->cacheKey('return:upload:'.$ref));if(!$upload||$upload['username']!==$account['username']||$upload['sourceId']!==(int)$out['id']||!is_file($this->stagingDirectory().$upload['name']))$this->fail('图片已失效或不属于本次来源');$uploads[$ref]=$upload;$path=Filesystem::disk('public')->putFileAs('wx_upload/'.date('Ymd').'/material_return',new \think\File($this->stagingDirectory().$upload['name']),$upload['name']);if(!$path)$this->fail('图片保存失败',503);$paths[]=$path;$image='/storage/'.str_replace('\\','/',$path);}$items[$id]=['source_material_id'=>$id,'name'=>$source[$id]['name'],'unit'=>$source[$id]['unit'],'type'=>$source[$id]['type'],'source_num'=>$source[$id]['original'],'num'=>$this->decimal($quantity),'mark'=>$this->text($item['mark']??'',255,true),'img'=>$image];}
                $row=['o_id'=>$out['id'],'owner'=>$admin['id'],'dpt'=>$admin['dpt'],'create_time'=>time(),'actual_back_time'=>$date->getTimestamp(),'apv'=>$apv,'mark'=>$this->text($this->request->post('mark',''),255,true),'status'=>1,'schema_version'=>1,'allBack'=>1];$id=Db::name('common_outplant_back')->insertGetId($row);try{foreach($items as $item){$item['return_id']=$id;Db::name('common_outplant_back_material')->insert($item);}}catch(\Throwable $e){Db::name('common_outplant_back_material')->where('return_id',$id)->delete();Db::name('common_outplant_back')->where('id',$id)->delete();throw $e;}$row['id']=$id;return $row;});}catch(\Throwable $e){foreach($paths as $path)Filesystem::disk('public')->delete($path);throw $e;}
            try{$this->cache()->set($receiptKey,['id'=>$row['id'],'hash'=>$hash],86400);}catch(\Throwable $e){Log::warning('return receipt unavailable');}foreach($uploads as $ref=>$upload){@unlink($this->stagingDirectory().$upload['name']);try{$this->cache()->delete($this->cacheKey('return:upload:'.$ref));}catch(\Throwable $e){Log::warning('return upload cleanup unavailable');}}
        }finally{Db::query('SELECT RELEASE_LOCK(?)',[$key]);}$this->notify($row,false);return $this->result(['id'=>(int)$row['id']]);
    });}
    public function approve(){return $this->guarded(function(){$this->requireMethod('POST');$account=$this->current();$this->admin($account);$decision=$this->request->post('decision');if(!in_array($decision,['pass','reject'],true))$this->fail('核验方式不正确');$reason=$decision==='reject'?$this->text($this->request->post('reason'),255):null;$row=$this->writeLock(function()use($account,$decision,$reason){$admin=$this->admin($account);$row=Db::name('common_outplant_back')->find($this->id($this->request->post('id')));if(!$row||(int)$row['schema_version']!==1||(int)$row['status']!==1||!empty($row['apv_time'])||(int)$row['apv']!==(int)$admin['id']||(int)$row['owner']===(int)$admin['id'])$this->fail('不是当前核验人或状态已变化',409);$out=$this->source($row['o_id']);$summary=$this->summaryFor($out,$account);if(!$this->sameDepartment($out,$admin)||$summary['blockReason']==='历史返厂待核对')$this->fail('部门或历史来源需核对',409);$changes=['status'=>$decision==='pass'?2:0,'apv_time'=>time(),'apv_mark'=>$reason];
            if($decision==='pass'){if((int)$out['status']!==4||(int)$out['backType']!==2||empty($out['check_time'])||empty($row['actual_back_time'])||(int)$row['actual_back_time']>time()||date('Y-m-d',$row['actual_back_time'])<date('Y-m-d',$out['check_time']))$this->fail('原单已失效',409);$sources=array_column($summary['materials'],null,'id');$items=Db::name('common_outplant_back_material')->where('return_id',$row['id'])->select()->toArray();if(!$items)$this->fail('缺少返厂明细',409);foreach($items as $item){$source=$sources[$item['source_material_id']]??null;if(!$source||$source['name']!==$item['name']||$source['unit']!==$item['unit']||$this->quantity($source['original'])!==$this->quantity($item['source_num'])||$this->quantity($item['num'])<=0||(int)$item['type']!==$source['type']||$this->quantity($item['num'])>$this->quantity($source['remaining']))$this->fail('原物料发生变化或数量超额，请驳回核对',409);}}
            try{Db::name('common_outplant_back')->where('id',$row['id'])->update($changes);if($decision==='pass'){$summary=$this->summaryFor($out,$account);if($summary['returnStatus']===1)$this->fail('未找到可核验数量',409);Db::name('common_outplant')->where('id',$out['id'])->update(['return_status'=>$summary['returnStatus']]);$changes['allBack']=$summary['returnStatus']===3?2:1;Db::name('common_outplant_back')->where('id',$row['id'])->update(['allBack'=>$changes['allBack']]);}}catch(\Throwable $e){Db::name('common_outplant_back')->where('id',$row['id'])->update(['status'=>$row['status'],'apv_time'=>$row['apv_time'],'apv_mark'=>$row['apv_mark'],'allBack'=>$row['allBack']]);Db::name('common_outplant')->where('id',$out['id'])->update(['return_status'=>$out['return_status']]);throw $e;}return array_merge($row,$changes);});$this->notify($row,true);return $this->result();});}
    public function remove(){return $this->guarded(function(){$this->requireMethod('POST');$account=$this->current();$this->writeLock(function()use($account){$admin=$this->admin($account);$row=Db::name('common_outplant_back')->find($this->id($this->request->post('id')));if(!$row||(int)$row['schema_version']!==1||(int)$row['owner']!==(int)$admin['id']||(int)$row['status']!==1||!empty($row['apv_time']))$this->fail('只能删除本人未核验登记',409);Db::name('common_outplant_back')->where('id',$row['id'])->update(['status'=>-1]);});return $this->result();});}
    protected function notify($row,$finished) {
        try {
            $service=$this->templateService();$page='pages/material-return/detail?id='.$row['id'];
            if ($finished) {
                $out=Db::name('common_outplant')->find($row['o_id']);
                return $service->send((int)$row['status']===0?'workflow_rejected':'workflow_approved',$service->admins([$row['owner'],$out['owner'] ?? 0]),[
                    'name'=>'物品返厂确认','approver'=>$service->adminName($row['apv']),'number'=>(string)$row['id'],'time'=>$service::time($row['apv_time']),
                ],$page);
            }
            return $service->send('workflow_pending',$service->admins([$row['apv']]),[
                'name'=>'物品返厂确认','initiator'=>$service->adminName($row['owner']),'time'=>$service::time($row['create_time']),
                'reason'=>$row['mark'] ?: '请核对返厂物料及数量','step'=>'主管确认',
            ],$page);
        } catch (\Throwable $error) { Log::warning('oms material return notification unavailable: '.$error->getMessage()); return false; }
    }
}
